如何在 fgets 循环中正确刷新标准输入

How to properly flush stdin in fgets loop

我在此处找到了几次使用 while((c = getchar()) != '\n' && c != EOF) 清除标准输入的示例,并尝试在通过 fgets 获取输入的循环中使用它。我需要刷新,因为循环从最后一个输入中获取 \n 字符并再次运行它。

所以现在我必须按两次回车键。为什么会发生这种情况,我该如何解决?

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

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

好吧,这就是您的代码正在做的事情 - 它首先逐个字符地读取,直到找到换行符。然后它调用 fgets() 这将......好吧,读取直到它找到一个换行符(可能是一个字符一个字符,但也可能以其他方式)。

您可以尝试 fflush(stdin),但不能保证达到您的要求(它只保证输出缓冲区,不保证输入)。

此外,您可以尝试 setbuf(stdin, NULL),它应该禁用标准输入的缓冲,因此不会刷新任何内容。我在不同的系统上尝试了几次并且它起作用了,但是这个功能的文档并不是 100% 清楚的。

不幸的是,在 C 语言中存在很多关于 "flushing input" 的混淆。这种混淆几乎完全来自关于流行但有缺陷的 scanf 函数的一个奇怪事实:它通常 not 读取整行输入,它通常在转换输入后在输入流上留下换行符 \n。例如,如果您编写一个程序说

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

如果用户键入“123”并按下 Return 键,数字 123 将存储到变量 n 中,但是 \n Return键对应的字符将留在输入流中。如果您的程序接下来要做的是调用 fgetsgetchar,假设您将开始读取用户键入的下一行输入,您的程序将立即读取剩余的换行符。看起来好像用户键入了额外的空白行或其他内容。

这个问题非常普遍。大量初学 C 的程序员都被它困住了。简而言之,推荐三种修复方法:

  1. 在调用像 scanf 这样的将换行符留在输入缓冲区中的函数之后,以及在调用像 getcharfgets 这样期望开始的函数之前在新行上,使用小循环 while((c = getchar()) != '\n' && c != EOF) 读取并丢弃 scanf 留在输入缓冲区中的换行符。

  2. 根本不要使用 scanf,或者如果你这样做,不要试图将它与对 getcharfgets 的调用混合使用。

  3. (流行但问题严重)调用 fflush(stdin) 清除不需要的输入。

#3 的问题已经被广泛讨论,所以我不会再多说那些问题或那个解决方案。推荐的 "portable" 替代方案是 #1,但它显然有效 仅当输入中至少有一个不需要的换行符等待刷新时 。否则,解决方案 #1 将错误地读取并丢弃一行想要的输入。

所以你不能到处使用#1。你不能把它随意地撒在你的程序中;你不能在任何你可能用过的地方使用它 fflush(stdin)。通常,如前所述,您仅在调用 scanf 之后和调用 getcharfgets.

之前才需要它

在问题的代码片段中,可能根本不需要使用解决方案#1。 fgets 函数完全有能力独自清晰地读取每一行输入。不需要额外刷新输入或丢弃换行符。 (但是,如果在 do_something() 下面调用了 scanf,则可能需要额外的换行处理。)

我的简单解决方法是创建一个缓冲区字符串 char clear[2] 并在我使用 scanf 时通过使用 gets(clear).[=13= 向其中写入换行符来刷新输入缓冲区]

的答案与 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);

在 while 循环中(在第一个 fgets() 之后),我首先检查输入没有换行(这意味着输入超过了输入长度)。然后我逐个删除一个字符,直到它到达一个新行。