0

I have been using the code

while (getchar() != '\n');

to clear everything in stdin in my programs. However, I am running into a slight issue on some occasions. There are times in my programs where when I call getchar(), there may or may not be anything in stdin. When it is the case that there is nothing in stdin, what ends up happening is getchar() is called and it is waiting for something in stdin. I then have to enter \n on my keyboard otherwise the program is just stuck on that one line. I am wondering if there is a simple way to first check if stdin is empty before doing while (getchar() != '\n'); in order to avoid this problem?

Not sure if the compiler matters for the answer, but I am using Visual Studio 2017. Edit: when I say stdin, I am referring to characters entered by the keyboard

  • 3
    Usually a similar loop (it should check for EOF too) is used *after* an attempted read from stdin (say, with scanf), to ignore whatever is left on said input line or drop it entirely if the read failed. – Bob__ Jul 04 '19 at 18:58
  • If the `stdin` input is redirected from a file, that makes no sense, so it would be from the keyboard, and Visual C has non-standard keyboard reading functions [`kbhit()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/posix-kbhit?view=vs-2017) and [`getch()`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getch?view=vs-2017). You can clear any input with `while(kbhit()) { getch(); }` or `while(_kbhit()) { _getch(); }` – Weather Vane Jul 04 '19 at 19:08
  • 1
    It's not that simple, but the "proper" answer is to use the `select` system call. You may find it easier to use the `poll` system call. If you know stdin is associated with a terminal (that is, a keyboard), you can use `ncurses` or `termio` to change the buffering behavior. – mpez0 Jul 04 '19 at 19:12
  • .. yes that does not clear `stdin` stream. – Weather Vane Jul 04 '19 at 19:14
  • 1
    One workaround is to obtain all input with `fgets()` and then process the string entered. – Weather Vane Jul 04 '19 at 19:16
  • I don't think there's a pure C answer to this. You might want to tag an OS platform (POSIX, Windows,...). – Petr Skocik Jul 04 '19 at 20:14
  • Bottom line, `getchar()` will block waiting on input if none is there. It is your job as the programmer to know when and where to place the loop to *empty_stdin*. This will occur after use of `scanf` with conversion specifiers that leave characters in `stdin`, or when using *line-oriented* input functions after checking that length of the string read completely fills the buffer and the last character is not the `'\n'` character. (there is still a corner-case when a file fails to contain a POSIX line-ending before `EOF` and the last line exactly fills the buffer) – David C. Rankin Jul 04 '19 at 21:23

3 Answers3

1

stdin is a stream. If a process is reading from console, as long as you don't close the stream, the process is still reading from the stream. From the terminal, you close the input stream by hitting ^D.

Application is not a mind reader. It cannot know when the stream is "DONE". If you want to read data in the stream and want to shutdown your process before the stream is not done, you need to set the stream to non-block. Read until nothing comes back and the process exits.

How do you do non-blocking console I/O on Linux in C?

Naoyuki Tai
  • 403
  • 4
  • 7
0

A long time ago I used to do something like this

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

int /* 0: success -1: error */
setBlockingFD(int fileDescriptor,
              int blocking)
{
  int r=fcntl(fileDescriptor,F_GETFL);
  if(r==-1)
  {
    perror("fcntl(F_GETFL)");
    return -1;
  }
  int flags=(blocking ? r & ~O_NONBLOCK : r | O_NONBLOCK);
  r=fcntl(fileDescriptor,F_SETFL,flags);
  if(r==-1)
  {
    perror("fcntl(F_SETFL)");
    return -1;
  }
  return 0;
}

void
discardInput(void)
{
  setBlockingFD(STDIN_FILENO, 0);
  for(;;)
  {
    int c=fgetc(stdin);
    if(c==EOF)
    {
      if(errno==EAGAIN) // just to understand the example
      {
        printf("nothing more in input\n");
      }
      break;
    }
    else
    {
      printf("discarding <%c>\n", c); // just to understand the example
    }
  }
  setBlockingFD(STDIN_FILENO, 1);
}

int
main(void)
{
  for(;;)
  {
    discardInput();
    printf("enter an integer and something else\n");
    int value;
    if(scanf("%d", &value)==1)
    {
      printf("value=%d\n", value);
    }
    else
    {
      printf("no integer\n");
      break;
    }
  }
  return 0;
}

When an input file descriptor is configured as non-blocking, it fails with errno set to EAGAIN when there is no pending input.

note: I prefer this solution to a select()-based one because select() won't be aware of the data that already stand in the buffer of the input stream (the FILE * not the file descriptor) and that must be discarded.

edit: this solution only works on POSIX systems; on Windows it only works with WSL.

prog-fh
  • 13,492
  • 1
  • 15
  • 30
0

Since you are on Windows with VS, use _kbhit() which checks if there are ny characters in the stdin.

#include <conio.h>

int main()
{
  if (_kbhit()) while (getchar() != '\n');
  ...
  return 0;
}
AndersK
  • 35,813
  • 6
  • 60
  • 86