提问人:ipkiss 提问时间:10/26/2011 最后编辑:digito_evoipkiss 更新时间:9/12/2023 访问量:486146
如何清除 C 语言中的输入缓冲区?
How can I clear an input buffer in C?
问:
我有以下程序:
int main(int argc, char *argv[])
{
char ch1, ch2;
printf("Input the first character:"); // Line 1
scanf("%c", &ch1);
printf("Input the second character:"); // Line 2
ch2 = getchar();
printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
printf("ch2=%c, ASCII code = %d\n", ch2, ch2);
system("PAUSE");
return 0;
}
正如上述代码的作者所解释的:
该程序将无法正常工作,因为在第 1 行,当用户按下时,它将在输入缓冲区中留下 2 个字符:键(ASCII 代码 13)和(ASCII 代码 10)。因此,在第 2 行中,它将读取 并且不会等待用户输入字符。EnterEnter
\n
\n
好的,我明白了。但我的第一个问题是:为什么第二个()不读 ,而不是字符?getchar()
ch2 = getchar();
Enter key (13)
\n
接下来,笔者提出了解决此类问题的两种方法:
用
fflush()
编写如下函数:
void
clear (void)
{
while ( getchar() != '\n' );
}
这段代码实际上有效。但我无法解释它是如何工作的。因为在 while 语句中,我们使用 ,这意味着读取除 ?如果是这样,字符是否仍保留在输入缓冲区中?getchar() != '\n'
'\n'
'\n'
答:
但我无法解释自己它是如何工作的?因为在 while 语句中,我们使用 ,这意味着读取除 ??如果是这样,则在输入缓冲区中仍保留该字符??? 我是不是误会了什么??
getchar() != '\n'
'\n'
'\n'
您可能没有意识到的是,比较是在从输入缓冲区中删除字符之后发生的。所以当你到达 时,它被消耗掉了,然后你就跳出了这个循环。getchar()
'\n'
这些行:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
;
不只读取换行符 () 之前的字符。它会读取流中的所有字符(并丢弃它们),直到并包括下一个换行符(或遇到 EOF)。要使测试为真,它必须首先读取换行符;因此,当循环停止时,换行符是读取的最后一个字符,但它已被读取。'\n'
至于为什么它读取换行符而不是回车符,那是因为系统已将回车符转换为换行符。当按下回车键时,表示行的结束......但流包含换行符,因为这是系统的正常行尾标记。这可能取决于平台。
此外,在输入流上使用并不适用于所有平台;例如,它通常不适用于 Linux。fflush()
评论
while ( getchar() != '\n' );
getchar()
EOF
int ch; while ((ch = getchar()) != '\n' && ch != EOF);
该程序将无法正常工作,因为在第 1 行,当用户按 Enter 键时,它将在输入缓冲区 2 中留下字符:Enter 键(ASCII 代码 13)和 \n(ASCII 代码 10)。因此,在第 2 行,它将读取 \n,并且不会等待用户输入字符。
您在第 2 行看到的行为是正确的,但这并不是正确的解释。对于文本模式流,您的平台使用什么行尾(无论是回车符 (0x0D) + 换行符 (0x0A)、裸 CR 还是裸 LF )都无关紧要。C 运行时库将为您处理这个问题:您的程序将只看到换行符。'\n'
如果键入一个字符并按 Enter,则该输入字符将按第 1 行读取,然后由第 2 行读取。请参阅我正在使用 scanf %
c 读取 Y/N 响应,但后来的输入被跳过。 来自 comp.lang.c 常见问题解答。'\n'
至于建议的解决方案,请参阅(再次来自 comp.lang.c 常见问题解答):
它基本上表明,唯一的可移植方法是:
int c;
while ((c = getchar()) != '\n' && c != EOF) { }
您的循环之所以有效,是因为一旦您调用,返回的字符就已经从输入流中删除了。getchar() != '\n'
getchar()
另外,我觉得有义务劝阻你不要完全使用:为什么每个人都说不要使用 scanf
?我应该用什么来代替?scanf
评论
getchar()
scanf(" %c", &char_var)
您也可以通过以下方式进行操作:
fseek(stdin,0,SEEK_END);
评论
fseek
stdin
unsigned char a=0;
if(kbhit()){
a=getch();
while(kbhit())
getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;
-或-
也许使用 ..???_getch_nolock()
评论
kbhit()
getch()
<conio.h>
清除您已经尝试部分阅读的行末尾的一种可移植方法是:
int c;
while ( (c = getchar()) != '\n' && c != EOF ) { }
这将读取并丢弃字符,直到它得到文件结束的信号。它还会检查输入流是否在行尾之前关闭。的类型必须为(或更大)才能保存值。\n
EOF
c
int
EOF
没有可移植的方法可以找出当前行之后是否还有更多行(如果没有,则将阻止输入)。getchar
评论
stdio.h
#define EOF (-1)
man getchar
EOF
getchar()
char(0,1,2,...255) + EOF
EOF
getchar(_)
int
C
int
你可以试试
scanf("%c%*c", &ch1);
其中 %*c 接受并忽略换行符。
还有一种方法
您可以编写
while((getchar()) != '\n');
不要忘记 while 循环后面的分号。
评论
"%*c"
while((getchar())!='\n');
getchar()
EOF
我在尝试实施解决方案时遇到问题
while ((c = getchar()) != '\n' && c != EOF) { }
我为可能有同样问题的人发布一些调整“代码 B”。
问题是程序让我独立于回车符捕获“\n”字符,这是给我带来问题的代码。
代码 A
int y;
printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
continue;
}
printf("\n%c\n", toupper(y));
调整是在 while 循环中的条件被计算之前“捕获”(n-1) 字符,下面是代码:
代码 B
int y, x;
printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
x = y;
}
printf("\n%c\n", toupper(x));
可能的解释是,要使 while 循环中断,它必须将值 '\n' 分配给变量 y,因此它将是最后一个赋值。
如果我错过了解释,代码 A 或代码 B,请告诉我,我对 c 几乎没有新手。
希望它对某人有所帮助
评论
ch=getchar())!='\n'&&ch!=EOF);
来咀嚼 stdin
输入缓冲区中的每个字符。while
stdin
y
\n
EOF
EOF
[ENTER]
简短、便携并在 stdio.h 中声明
stdin = freopen(NULL,"r",stdin);
当 stdin 上没有任何东西可以刷新时,不会像以下众所周知的行一样刷新,从而不会挂在无限循环中:
while ((c = getchar()) != '\n' && c != EOF) { }
有点贵,所以不要在需要反复清除缓冲区的程序中使用它。
从同事那里偷来的:)
评论
freopen
stdin
另一个尚未提到的解决方案是使用: 倒带(stdin);
评论
试试这个:
stdin->_IO_read_ptr = stdin->_IO_read_end;
评论
我很惊讶没有人提到这一点:
scanf("%*[^\n]");
简而言之。把线...
while ((c = getchar()) != '\n') ;
...在读取输入的行之前,输入是唯一有保证的方法。 它仅使用核心 C 功能(根据 K&R 的“C 语言的传统核心”),保证在所有情况下都适用于所有编译器。
实际上,您可以选择添加提示,要求用户按 Enter 键继续(或者,可以选择按 Ctrl-D 或任何其他按钮来完成或执行其他代码):
printf("\nPress ENTER to continue, press CTRL-D when finished\n");
while ((c = getchar()) != '\n') {
if (c == EOF) {
printf("\nEnd of program, exiting now...\n");
return 0;
}
...
}
仍然存在一个问题。用户可以多次按 Enter。这可以通过在输入行中添加输入检查来解决:
while ((ch2 = getchar()) < 'a' || ch1 > 'Z') ;
从理论上讲,上述两种方法的结合应该是无懈可击的。 在所有其他方面,@jamesdlin的答案是最全面的。
快速解决方案是添加第二个扫描,以便它迫使 ch2 暂时吃掉回车。不做任何检查,所以它假设用户会玩得很好。没有完全清除输入缓冲区,但它工作得很好。
int main(int argc, char *argv[])
{
char ch1, ch2;
printf("Input the first character:"); // Line 1
scanf("%c", &ch1);
scanf("%c", &ch2); // This eats '\n' and allows you to enter your input
printf("Input the second character:"); // Line 2
ch2 = getchar();
printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
printf("ch2=%c, ASCII code = %d\n", ch2, ch2);
system("PAUSE");
return 0;
}
scanf
是一个奇怪的功能,电影《战争游戏》中有一句经典台词是相关的:“唯一的制胜之举就是不玩”。
如果你发现自己需要“刷新输入”,你就已经输了。制胜之举不是拼命寻找某种神奇的方法来刷新荨麻输入:相反,你需要做的是以某种不同的(更好的)方式进行输入,这不涉及在输入流上留下未读的输入,并让它坐在那里并引起问题,因此你必须尝试刷新它。
基本上有三种情况:
您正在使用 读取输入,并且它将用户的换行符保留在输入流上,并且该杂散换行符被稍后对 或 的调用错误地读取。(这是您最初询问的情况。
scanf
getchar
fgets
您正在使用 读取输入,并且它将用户的换行符保留在输入流上,并且该杂散换行符被稍后调用 错误地读取。
scanf
scanf("%c")
您正在使用 读取数字输入,并且用户正在键入非数字文本,并且非数字文本留在输入流中,这意味着下一次调用也会失败。
scanf
scanf
在所有三种情况下,正确的做法似乎是“刷新”有问题的输入。你可以尝试,但充其量是繁琐的,最坏的情况是不可能的。最后,我认为尝试刷新输入是错误的方法,并且有更好的方法,具体取决于您担心的情况:
在情况 1 中,更好的解决方案是,不要将对 scanf
的调用与其他输入函数混合使用。要么用 做所有输入,要么用 和/或做所有输入。要用 进行所有输入,可以将对 的调用替换为 — 但请参阅第 2 点。从理论上讲,您可以将调用替换为 ,尽管这有各种进一步的问题,我不建议这样做。即使您想要/需要一些 的解析,也要完成所有输入,您可以使用读取行,然后在事后使用 .scanf
getchar
fgets
scanf
getchar
scanf("%c")
fgets
scanf("%[^\n]%*c")
fgets
scanf
fgets
sscanf
在情况 2 中,更好的解决方案是永远不要使用 scanf(“%c”)。
请改用。神奇的额外空间让一切变得不同。(关于这个额外的空间有什么作用以及为什么它有帮助,有一个很长的解释,但这超出了这个答案的范围。scanf(" %c")
在案例 3 中,恐怕根本没有好的解决方案。 有很多问题,它的众多问题之一就是它的错误处理很糟糕。如果你想编写一个读取数字输入的简单程序,并且如果你可以假设用户在出现提示时总是会输入正确的数字输入,那么可以是一个足够的——勉强足够的——输入法。但也许你的目标是做得更好。也许您想提示用户输入一些数字输入,并检查用户是否确实输入了数字输入,如果没有,请打印错误消息并要求用户重试。在这种情况下,我相信无论出于何种意图和目的,您都无法基于 scanf
实现此目标。你可以尝试,但这就像给一个蠕动的婴儿穿上连体衣:在把两条腿和一只胳膊伸进去后,当你试图把第二只胳膊伸进去时,一条腿会蠕动出来。尝试使用 读取和验证可能不正确的数字输入实在是太费力了。以其他方式做到这一点要容易得多。scanf
scanf("%d")
scanf
你会注意到一个贯穿我列出的所有三个案例的主题:它们都以“您正在使用...”阅读输入开始。这里有一个不可避免的结论。请参阅另一个问题:我可以使用什么来代替 scanf 进行输入转换?scanf
现在,我意识到我还没有回答你实际提出的问题。当人们问“我该如何做X?”时,当所有的答案都是“你不应该想做X”时,这真的很令人沮丧。如果你真的,真的想刷新输入,那么除了人们在这里给你的其他答案之外,还有两个充满相关答案的好问题:
我编写了一个函数(并对其进行了测试),该函数从 stdin 获取输入并丢弃额外的输入(字符)。此函数称为 get_input_from_stdin_and_discard_extra_characters(char *str, int size),它读取最大“size - 1”个字符,并在末尾附加一个“\0”。
代码如下:
/* read at most size - 1 characters. */
char *get_input_from_stdin_and_discard_extra_characters(char *str, int size)
{
char c = 0;
int num_chars_to_read = size - 1;
int i = 0;
if (!str)
return NULL;
if (num_chars_to_read <= 0)
return NULL;
for (i = 0; i < num_chars_to_read; i++) {
c = getchar();
if ((c == '\n') || (c == EOF)) {
str[i] = 0;
return str;
}
str[i] = c;
} // end of for loop
str[i] = 0;
// discard rest of input
while ((c = getchar()) && (c != '\n') && (c != EOF));
return str;
} // end of get_input_from_stdin_and_discard_extra_characters
如何在 C 语言中刷新或清除输入缓冲区?stdin
cppreference.com 社区 wiki 上的 fflush()
引用指出(强调后加):
对于输入流(以及输入最后一个操作的更新流),行为是未定义的。
所以,不要在 上使用 。fflush()
stdin
如果你的“刷新”目标是删除缓冲区中的所有字符,那么最好的方法是手动使用 getchar()
或 getc(stdin
)(同样的事情),或者如果使用 POSIX 或 Linux,则使用 read()(
使用 stdin
作为第一个参数)。stdin
stdin
int c;
while ((c = getchar()) != '\n' && c != EOF);
我认为更清晰(更易读)的方法是这样做。当然,我的评论使我的方法看起来比实际要长得多:
/// Clear the stdin input stream by reading and discarding all incoming chars up
/// to and including the Enter key's newline ('\n') char. Once we hit the
/// newline char, stop calling `getc()`, as calls to `getc()` beyond that will
/// block again, waiting for more user input.
/// - I copied this function
/// from "eRCaGuy_hello_world/c/read_stdin_getc_until_only_enter_key.c".
void clear_stdin()
{
// keep reading 1 more char as long as the end of the stream, indicated by
// `EOF` (end of file), and the end of the line, indicated by the newline
// char inserted into the stream when you pressed Enter, have NOT been
// reached
while (true)
{
int c = getc(stdin);
if (c == EOF || c == '\n')
{
break;
}
}
}
例如,我在这里的两个文件中使用了这个函数。请参阅以下文件中的上下文,了解何时清除可能最有用:stdin
自我注意:我最初在这里发布了这个答案,但后来删除了这个答案,将这个答案作为我唯一的答案。
为了完整起见,在你的情况下,如果你真的想使用,有很多理由不这样做,我会在格式说明符前面添加一个空格,告诉忽略字符前面的所有空格:scanf
scanf
scanf(" %c", &ch1);
在此处查看更多详细信息:https://en.cppreference.com/w/c/io/fscanf#Notes
首先,您应该使用 int 类型的变量而不是 char 来读取输入输出流(至少在 Linux 中)。
其次,在 Linux 中第一次读取后,stdin 中只剩下 '\n'(在 Windows 中有所不同)。因此,您可以在示例中首次读取后使用额外的变量来清除缓冲区。例如,您的代码可以如下所示:
int cr;
cr = fgetc(stdin);
以下操作在 Linux 上执行。
fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
while(getchar() != EOF)
;
评论
\r
\r
\r\n
\n
\r\n
\n