19

I have read about 5-10 different advices how to clear stdin, but none of them suits my needs. The thing is that fflush(stdin) worked perfectly at my computer, but unfortunately it doesn't seem to work everywhere, so I need something with the same functionality. Every other way I tried clears stdin when it is not empty but requires user input when stdin IS empty, which means it requires input in a moment I dont want to get any (+ it discards it anyway).

The question is: Can I somehow make sure, that stdin IS empty before I require user input? (and if not, THEN and only then clear it somehow?) something like:

if (stdin is NOT empty) while (getchar() != '\n') continue; 

EDIT: the thing is that I load characters from stdin one by one and at some point, a part of the input from previous iteration might or might not get discarded. either way, I need to have clear stdin before I ask the user for another input to be processed. Clearing the buffer itself is not such a big deal, the problem is what happens when the input is empty when the program gets to the point of clearing stdin, because in that moment the program needs another input which is going to be eaten by the clearing function. Thats what I want to get rid of. (when I could use fflush(stdin); I just knew, that for the next line of my program the stdin WILL be empty no matter what, no questions asked...)

5
  • 6
    fflush(stdin) is UB. Commented Apr 19, 2016 at 10:00
  • 1
    There is no such thing as an empty I/O stream , it is a stream. Consider: ./myprog </dev/random ; will it ever see the end of its stdin stream? Commented Apr 19, 2016 at 10:28
  • programming it on windows, has to work at linux... Commented Apr 19, 2016 at 11:18
  • Do you want to flush any partially-typed lines of input (that is, input for which the user hasn't typed ENTER to commit) as well? Commented Apr 19, 2016 at 17:37
  • 1
    @Jens The apt-get program does this, intentionally. See Prevent sudo, apt-get, etc. from swallowing pasted input to STDIN Commented Apr 20, 2016 at 15:21

6 Answers 6

10

How to clear stdin before getting new input?
.. so I need something with the same functionality.

With portable C this is not possible.


Instead suggest a different (and more usual C) paradigm:
Insure previous input functions consumes all the previous input.

fgets() (or *nix getline()) is the typical approach and solves most situations.

Or roll your own. The following reads an entire line, but does not save extra input.

int mygetline(char *buf, size_t size) { assert(size > 0 && size <= INT_MAX); size_t i = 0; int ch; while ((ch = fgetc(stdin)) != EOF) { // Read until EOF ... if (i + 1 < size) { buf[i++] = ch; } if (ch == '\n') { // ... or end of line break; } } buf[i] = '\0'; if (i == 0) { return EOF; } return i; } 
Sign up to request clarification or add additional context in comments.

Comments

1

From a similar question, Use poll() with fds.fd set to 0 (stdin), fds.events set to POLLIN, nfds set to 1, and timeout set to zero. After calling poll(), fds.revents will be set to zero if the buffer is empty, and to POLLIN otherwise.

struct pollfd fds = {0, POLLIN, 0}; poll(&fds, 1, 0); if(fds.revents == POLLIN} printf("stdin buffer is not empty"); 

This solution will work on posix-compliant systems, but not Windows. Use select() for portability.

Comments

0

TL;DR fflush(stdin) invokes undefined behavior as per the standard, you should never use it.


Coming to your code (logic), instead of looking for a newline, you can look for EOF. It does not have a prerequisite that stdin should have some input before running this loop.

Something like

 while (getchar() != EOF); //; is not a mistake 

should meet your needs.

3 Comments

while (getchar() != EOF); sets me in an infinite loop requiring another and another input. I also tried if (!feof(stdin)) etc. but none of that worked. It seemes like stdin does not have any EOF set, it only requires another input when the end is reached.
At least at windows you need to explicitly generate EOF for stdio with Ctrl+Z.
Er, no. Your loop waits until the user generates an EOF (^D or ^Z), while the OP asked for a way to flush the input buffer.
0

Use only fgets() to read stdin.

Use a large enough buffer and/or test for full lines.

Using fgets() you never have to worry about extra characters in stdin.

// read characters until 'X' while (((ch = getchar()) != EOF) && (ch != 'X')) putchar(ch); // discard X and the rest of the line fflush(stdin); // UB except for Windows 

// read full line char tmp[1000], *p; if (!fgets(tmp, sizeof tmp, stdin)) /* deal with error */; if (!*tmp) /* embedded NUL detected: input is not a text file */; if (tmp[strlen(tmp) - 1] != '\n') /* partial line */; p = tmp; while (*p && *p != 'X') putchar(*p++); // ignore the X and all the subsequent characters 

3 Comments

if (tmp[strlen(tmp) - 1] != '\n') is UB if tmp[0] == 0. Easy enough to have the first character read by fgets() to be the null character.
@chux: right, but not on text files. Text files (stdin, the keyboard, ...) do not have embedded NULs. Code changed to deal with that anyway. Thanks
True, it is not common for the first character to be the null character. The definition of a text file is not formal and often does not expressly preclude a '\0'. Yet it is not rare either to read a UTF-16BE text file that lacks a BOM (thinking it is a simple ASCII) file which can easily have a leading null character. If a keyboard can generate a null character or not is a platform level issue, beyond the program's control. IMO it is a hacker exploit and robust code copes. up vote for improved answer.
0

The following implementation of getline in standard C shows how to clear stdin :

/* read a line into s, return length */ int getline (char line[], int max_length) { int i; char c; for (i = 0; i < max_length && (c = getchar()) != '\n'; ++i) line[i] = c; line[i] = '\n'; line[i+1] = '\0'; /* clear stdin if not empty (user entry exceeded max_length */ if (c != '\n') while (getchar() != '\n') ; return i; } 

1 Comment

Note that when getchar() returns EOF due to end-of-file, this getline() leads to an infinite loop.
-4

The select module offers a function called select that achieves exactly what you're looking for. select.select takes three arguments:

select.select(rlist, wlist, xlist) 

Each argument should be a list of file descriptors (such as [sys.sdtin]) and it then waits until a specific IO operation is available. The IO operations are read, write or some other exception on the given file descriptors. It returns a tuple of corresponding lists populated with the file descriptors that are ready.

So, if there is input waiting in sys.stdin then the function would behave like so:

>>> import select >>> import sys >>> >>> select.select([sys.stdin], [], []) ([sys.stdin], [], []) >>> 

By itself, this doesn't solve your problem because by default the function will wait until an IO operation is available. Importantly, however, select.select has an optional timeout argument denoting how long it will wait before giving up. We simply have to set the timeout to zero and we can check for input without blocking the program flow.

Let's see an example where there is no input waiting in sys.stdin:

>>> import select >>> import sys >>> >>> timeout = 0 >>> select.select([sys.stdin], [], [], timeout) ([], [], []) >>> 

Knowing that we only want the first element of that tuple (the input streams) we're ready to make a useful if statement:

if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: print('Input is waiting to be read.') 

That means clearing the input stream just needs some iteration:

while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: sys.stdin.readline() 

And we can of course use this on any input stream, so lets put it in a function:

def clear_input(stream, timeout=0): '''Takes an input stream and discards each line in the buffer. The given timeout denotes how long in seconds to wait for further input when none is available. ''' while stream in select.select([stream], [], [], timeout)[0]: stream.readline() 

So let's demonstrate our function to achieve what you ask for in your question:

import select import sys import time def clear_input(stream, timeout=0): while stream in select.select([stream], [], [], timeout)[0]: stream.readline() if __name__ == '__main__': print('Type some lines now. They will be ignored.') time.sleep(5) print('Clearing input.') clear_input(sys.stdin) user_input = raw_input('Please give some fresh input: ') print(user_input) 

The clear_input function can be used as a non-blocking way to clear input streams and should work in Python2 and Python3.

6 Comments

But you did notice that this is a C question, not a python question?
Great answer, though. Just needs a different question.
I suppose the TLDR answer is "use Python" :-)
Was looking for a Python solution. Unfortunately, this doesn't work on Windows. I receive: OSError: [WinError 10038] An operation was attempted on something that is not a socket.
@howdoicode it looks like you need this answer: stackoverflow.com/a/46811177
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.