Question

I am a programming student looking for a way to get rid of characters that may be hanging around in stdin. I have tried a technique that has been given here in various forms, where you do something like this:

void clearStdIn(void) 
{
    char c;
    while((c = getchar()) != '\n' && c != EOF)
        /* discard */ ;
}

The problem seems to be that if nothing is in stdin to begin with, this function sits around waiting for the user to hit enter before control flow can move on. What should I do?

Was it helpful?

Solution

Flushing an input stream (in a portable way) without blocking could be done like this:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int flush_inputstream(int fd)
{
  int result = 0;

  int flags = fcntl(fd, F_GETFL);
  if (-1 == flags)
  {
    perror("fcntl() failed getting flags");

    result = -1;
    goto lblExit;
  }

  if (!(flags & O_NONBLOCK)) /* If stream isn't non-blocking */
  {                          /* set it to be non-blocking. */
    result = fcntl(fd, F_SETFL, O_NONBLOCK);
    if (-1 == result)
    {
      perror("fcntl() failed setting O_NONBLOCK");

      goto lblExit;
    }
  }

  /* Loop reading from the stream until it is emtpy: */
  do
  {
    char c = 0;
    ssize_t bytesRead = read(fd, &c, 1);
    if (-1 == bytesRead)
    {
      if ((EAGAIN != errno) && (EWOULDBLOCK != errno))
      {
        perror("read() failed");

        result = -1;
      }

      break;
    }
  } while (1);

  if (!(flags & O_NONBLOCK)) /* If stream had not be non-blocking */
  {                          /* re-set it to not be non-blocking. */
    int result_fcntl = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
    if (-1 == result_fcntl)
    {
      perror("fcntl() failed setting flags");

      if (0 == result) /* Do not overwrite prvious error! */
      {
        result = result_fcntl;
      }

      goto lblExit;
    }
  }

lblExit:

  return result;
}

/* To test this: */
int main(void)
{
  int fd = fileno(stdin);

  printf("Feed some chars via the keyboard now!\n");

  sleep(3);

  printf("Game Over! Press enter to see stdin is empty\n");

  if (-1 == flush_inputstream(fd))
  {
    fprintf(stderr, "flush_inputstream() failed");
    return EXIT_FAILURE;
  }

  char s[16] = "";
  if (NULL == fgets(s, sizeof(s), stdin))
  {
    perror("fgets() failed");
  }

  printf("%s\n", s);

  return EXIT_SUCCESS;
}

OTHER TIPS

There are 2 separate issues. The first is something I'd call flow control. In situations where the "sender" (e.g. a fast typist using a console application) sends data faster than the "receiver" (e.g. slow console application software) can receive it, you do not want to lose data and do want to have a buffer to prevent data loss.

For a simple example, if your lecturer is as smart as they should be, they'll probably make their own life easier by using automated script to pre-test student's assignments. In this case STDIN would actually be a file (and data from STDOUT would be checked to see if it matches an expected pattern). By discarding everything in STDIN you'd discard all input and fail.

The second issue is keeping the application synchronised with its input. This is normally done by fetching a line of user input (then parsing it and doing something with the parsed data), then fetching the next line, etc. If you do this wrong (e.g. only fetch part of a line of input) then you need to find a way to handle any characters between the end of what you fetched and the end of the line. The simple solution here is don't do it wrong to begin with - e.g. use a "fetch entire line of user input" function (maybe gets()) and worry about parsing the line of user input after you've obtained it.

Also note that sane software will handle unwanted/unexpected characters by generating some sort of an "unknown characters after ..." error message, not by discarding/ignoring them. It's best to do this during parsing, not after parsing when you're doing something with data from (probably wrong) user input. For a simple example, imagine "enter your age: 22 months" - in this case you're expecting the user to enter years, you see "22" then see "months" then generate an error ("Bad input - please enter 'age' as a single number of years"). You don't just assume the user wanted 22 years.

There is no portable way of doing this, since the standard input is "blocking" by default you can't read from it in order to throw the input away without causing a block if there was no input.

If you have it, you can use select() to detect that there is input, and then do a read to discard it. I would recommend doing a "large" block read in that case, not a single character at a time.

Please note that getchar() returns int, the constant EOF does not fit in a character. It's an example of "out of band" communication, it's not a character.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top