How to properly flush stdin in fgets loop

余生长醉 提交于 2020-01-21 12:51:12

问题


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

回答1:


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.




回答2:


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 inout, 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 ffgets.

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



来源:https://stackoverflow.com/questions/34219549/how-to-properly-flush-stdin-in-fgets-loop

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!