如何在 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键对应的字符将留在输入流中。如果您的程序接下来要做的是调用 fgets
或 getchar
,假设您将开始读取用户键入的下一行输入,您的程序将立即读取剩余的换行符。看起来好像用户键入了额外的空白行或其他内容。
这个问题非常普遍。大量初学 C 的程序员都被它困住了。简而言之,推荐三种修复方法:
在调用像 scanf
这样的将换行符留在输入缓冲区中的函数之后,以及在调用像 getchar
或 fgets
这样期望开始的函数之前在新行上,使用小循环 while((c = getchar()) != '\n' && c != EOF)
读取并丢弃 scanf
留在输入缓冲区中的换行符。
根本不要使用 scanf
,或者如果你这样做,不要试图将它与对 getchar
或 fgets
的调用混合使用。
(流行但问题严重)调用 fflush(stdin)
清除不需要的输入。
#3 的问题已经被广泛讨论,所以我不会再多说那些问题或那个解决方案。推荐的 "portable" 替代方案是 #1,但它显然有效 仅当输入中至少有一个不需要的换行符等待刷新时 。否则,解决方案 #1 将错误地读取并丢弃一行想要的输入。
所以你不能到处使用#1。你不能把它随意地撒在你的程序中;你不能在任何你可能用过的地方使用它 fflush(stdin)
。通常,如前所述,您仅在调用 scanf
之后和调用 getchar
或 fgets
.
之前才需要它
在问题的代码片段中,可能根本不需要使用解决方案#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() 之后),我首先检查输入没有换行(这意味着输入超过了输入长度)。然后我逐个删除一个字符,直到它到达一个新行。
我在此处找到了几次使用 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键对应的字符将留在输入流中。如果您的程序接下来要做的是调用 fgets
或 getchar
,假设您将开始读取用户键入的下一行输入,您的程序将立即读取剩余的换行符。看起来好像用户键入了额外的空白行或其他内容。
这个问题非常普遍。大量初学 C 的程序员都被它困住了。简而言之,推荐三种修复方法:
在调用像
scanf
这样的将换行符留在输入缓冲区中的函数之后,以及在调用像getchar
或fgets
这样期望开始的函数之前在新行上,使用小循环while((c = getchar()) != '\n' && c != EOF)
读取并丢弃scanf
留在输入缓冲区中的换行符。根本不要使用
scanf
,或者如果你这样做,不要试图将它与对getchar
或fgets
的调用混合使用。(流行但问题严重)调用
fflush(stdin)
清除不需要的输入。
#3 的问题已经被广泛讨论,所以我不会再多说那些问题或那个解决方案。推荐的 "portable" 替代方案是 #1,但它显然有效 仅当输入中至少有一个不需要的换行符等待刷新时 。否则,解决方案 #1 将错误地读取并丢弃一行想要的输入。
所以你不能到处使用#1。你不能把它随意地撒在你的程序中;你不能在任何你可能用过的地方使用它 fflush(stdin)
。通常,如前所述,您仅在调用 scanf
之后和调用 getchar
或 fgets
.
在问题的代码片段中,可能根本不需要使用解决方案#1。 fgets
函数完全有能力独自清晰地读取每一行输入。不需要额外刷新输入或丢弃换行符。 (但是,如果在 do_something()
下面调用了 scanf
,则可能需要额外的换行处理。)
我的简单解决方法是创建一个缓冲区字符串 char clear[2]
并在我使用 scanf
时通过使用 gets(clear)
.[=13= 向其中写入换行符来刷新输入缓冲区]
将
#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() 之后),我首先检查输入没有换行(这意味着输入超过了输入长度)。然后我逐个删除一个字符,直到它到达一个新行。