Tom
Tom

Reputation: 213

How to clear stdin before getting new input?

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...)

Upvotes: 18

Views: 40158

Answers (6)

dabeaumo
dabeaumo

Reputation: 1

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;
}

Upvotes: 0

Jason Fry
Jason Fry

Reputation: 11

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.

Upvotes: 1

chux
chux

Reputation: 153417

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;
}

Upvotes: 9

pmg
pmg

Reputation: 108978

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

Upvotes: 0

QuiteClose
QuiteClose

Reputation: 686

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.

Upvotes: -4

Sourav Ghosh
Sourav Ghosh

Reputation: 134316

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.

Upvotes: 0

Related Questions