10

I have found the example of clearing stdin using while((c = getchar()) != '\n' && c != EOF) on here a few times, and tried to use it in a loop that gets input via fgets. I need to flush, since the loop takes the \n character from the last input and runs with it again.

So what happens is that I have to press enter twice now. Why is this happening, and how can I fix this?

#define BUFFER_LIMIT 50
do
{
  int c;
  while ((c = getchar()) != '\n' && c != EOF);

  printf("console> ");
  fgets(input_buffer, BUFFER_LIMIT-1, stdin);

  if(do_something(input_buffer))
    break;
} while(strncmp(input_buffer, "quit", 4) != 0);
BigBadWolf
  • 603
  • 2
  • 8
  • 17

4 Answers4

10

There is unfortunately a lot of confusion about "flushing input" in C. The confusion arises almost entirely from one of the strange facts about the popular-but-flawed scanf function: it generally does not read full lines of input, and it generally leaves the newline character \n on the input stream after it converts its input. For example, if you write a program that says

printf("Type a number:\n");
scanf("%d", &n);

and if the user types "123" and hits the Return key, the number 123 will get stored into the variable n, but the \n character corresponding to the Return key will be left on the input stream. If the next thing your program does is to call fgets, or getchar, imagining that you'll begin reading the next line of input the user typed, your program will instead immediately read that leftover newline. It will seem as if the user typed an extra blank line, or something.

This problem is ridiculously widespread. Vast numbers of beginning C programmers have gotten stuck on it. In a nutshell, there are three recommended ways of fixing it:

  1. After calling a function like scanf that leaves the newline in the input buffer, and before calling a function like getchar or fgets that expects to start on a new line, use the little loop while((c = getchar()) != '\n' && c != EOF) to read and discard the newline that scanf left on the input buffer.

  2. Don't use scanf at all, or if you do, don't try to mix it with calls to getchar or fgets.

  3. (popular but badly problematic) Call fflush(stdin) to flush the unwanted input.

The problems with #3 have been widely discussed, so I won't say any more about those problems or that solution. The recommended "portable" alternative is #1, but it obviously works only if there is at least one unwanted newline in the input waiting to be flushed. If not, solution #1 will wrongly read and discard a line of wanted input, instead.

So you can't use #1 everywhere. You can't sprinkle it randomly in your program; you can't use it everywhere you might have used fflush(stdin). In general, as mentioned already, you'll need it only after calling scanf and before calling getchar or fgets.

In the code fragment in the question, there may not have been a need to use solution #1 at all. The fgets function is perfectly capable of cleanly reading individual lines of input all by itself. No additional flushing of input or discarding of newlines is necessarily required. (If there was a call to scanf down underneath do_something(), however, additional newline handling might have been necessary.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • #1 is easily fixed by writing what you actually want it to do. You can even make consumption of the newline part of the `scanf` by adding `%*1[\n]` or similar to the end of the conversion string. – R.. GitHub STOP HELPING ICE Nov 30 '19 at 19:03
  • @R.. IMO, that particluar`scanf` trick is perfectly self-defeating. `scnaf` has only one virtue, and that is that it is superficially easy to use and understand by beginning programmers in their first few programs. But `%*1[\n]` is obviously the opposite of "easy to use and understand"! – Steve Summit Nov 30 '19 at 19:17
  • I love that my old question till gets attention! Maybe you could edit your answer with a code snippet that shows how #1 is properly done if you have the time? – BigBadWolf Feb 16 '20 at 08:15
8

So what happens is that I have to press enter twice now. Why is this happening, and how can I fix this?

Well, that is what your code is doing - it first reads char-by-char until it finds newline. Then it calls fgets() which will... well, read until it finds a newline (probably char-by-char, but, also possibly in some other way).

You could try fflush(stdin), but that is not guaranteed to do what you want (it only gives guarantees for output buffers, not for input).

Also, you may try setbuf(stdin, NULL) which should disable buffering on standard input, so there would be nothing to flush. I tried this a few times on different systems and it worked, but documentation for this function is not 100% clear on this.

srdjan.veljkovic
  • 2,468
  • 16
  • 24
  • Hm thanks.. fflush(stdin) isn't very portable though, is it? and setbuf(stdin, NULL) seems like a good solution! Is there anything about it that you could see going wrong? (except for it being not 100% clear) – BigBadWolf Dec 13 '15 at 12:57
  • 1
    @BigBadWolf `fflush(stdin)` is worth trying if you're not writing portable code, it might work for you (on your platform). As for `setbuf(stdin, NULL)` I don't see anything wrong with it, but, given its unclear documentation, some implementation might not do what you want. Basically, I'd try both and hopefully one will work out. :) – srdjan.veljkovic Dec 14 '15 at 15:30
  • Thanks a lot! My main reason to learn C is for engineering, which is why portability and documentation have high priority to me. Then again, I probably won't need an input loop on any embedded system :) – BigBadWolf Dec 15 '15 at 15:24
  • Belated comment: `fflush(stdin)` is of course not a good or generally recommended way of flushing input, and mucking around with `setbuf` is even worse. The right answer to the original question is that the original question is flawed: in a loop that's only calling `fgets`, there's no need to flush anything. The code in the question probably arose when an unnecessary and do-nothing call to `fflush(stdin)` was replaced with the "more portable" explicit `getchar` loop. – Steve Summit Nov 30 '19 at 18:57
1

My easy fix is making a buffer string char clear[2] and flushing the input buffer by writing the newline character to it whenever I use scanf by using gets(clear).

gr33nd00r
  • 11
  • 1
0

Using @gr33nd00r's answer with fgets().

#define BUFFER_LIMIT 50
do
{
  char input_buffer[BUFFER_LIMIT], clear[2];

  printf("console> ");
  fgets(input_buffer, BUFFER_LIMIT-1, stdin);
  while (strchr(input_buffer, '\n') == NULL && clear[0] != '\n')
      fgets(clear, 2, stdin);
  clear[0] = 0;

  if(do_something(input_buffer))
    break;
} while(strncmp(input_buffer, "quit", 4) != 0);

In the while loop (after the first fgets()), I first check that the input doesn't have a new line (which means the input exceeded the input length). Then I remove char by char, till it reaches a new line.

MA Bisk
  • 80
  • 6