如何清除C中的输入缓冲区?

ipk*_*iss 70 c buffer

我有以下程序:

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;
}
Run Code Online (Sandbox Code Playgroud)

正如上面代码的作者所解释的那样:程序将无法正常工作,因为在第1行,当用户按下Enter时,它将在输入缓冲区中留下2个字符:Enter key (ASCII code 13)\n (ASCII code 10).因此,在第2行,它将读取\n并且不会等待用户输入字符.

好的,我明白了.但我的第一个问题是:为什么第二个getchar()(ch2 = getchar();)不读取Enter key (13)而不是\n字符?

接下来,作者提出了两种解决此类问题的方法:

  1. 使用 fflush()

  2. 写一个这样的函数:

    void
    clear (void)
    {    
      while ( getchar() != '\n' );
    }
    
    Run Code Online (Sandbox Code Playgroud)

这段代码实际上有效.但我无法解释自己是如何运作的?因为在while语句中,我们使用getchar() != '\n',这意味着读取任何单个字符除外'\n'?如果是这样,在输入缓冲区仍然是'\n'字符?

jam*_*lin 78

程序将无法正常工作,因为在第1行,当用户按Enter键时,它将在输入缓冲区中保留2个字符:输入密钥(ASCII代码13)和\n(ASCII代码10).因此,在第2行,它将读取\n并且不会等待用户输入字符.

您在第2行看到的行为是正确的,但这不是正确的解释.对于文本模式流,平台使用的行结尾无关紧要(无论是回车(0x0D)+换行(0x0A),裸CR还是裸LF).C运行时库将为您处理:您的程序只会看到'\n'换行符.

如果您键入了一个字符并按下了Enter,那么该输入字符将被第1行读取,然后'\n'将被第2行读取.请参阅我正在使用scanf %c读取Y/N响应,但后来的输入被跳过.来自comp.lang.c FAQ.

至于提出的解决方案,请参阅(再次从comp.lang.c常见问题解答):

这基本上表明唯一可移植的方法是:

int c;
while ((c = getchar()) != '\n' && c != EOF) { }
Run Code Online (Sandbox Code Playgroud)

您的getchar() != '\n'循环有效,因为一旦调用getchar(),返回的字符已经从输入流中删除.

另外,我觉得有必要阻止你scanf完全使用:为什么每个人都说不使用scanf?我应该用什么呢?

  • 代码段`while((c = getchar())!='\ n'&& c!= EOF){}`缺少重要的c声明为int。建议添加`int c;` (3认同)
  • @jamesdlin 在 Linux 上,getchar() 将等待用户按 Enter 键。只有这样他们才会跳出 while 循环。据我所知,termios 是一个标准库。 (2认同)
  • @ishmael 不,你在这两方面都错了。这个问题是关于如何在读取输入后从标准输入中清除剩余的输入,并且在标准 C 中,提供输入*首先*需要键入一行并按 Enter 键。*输入该行后,`getchar()` 将读取并返回这些字符;用户无需再次按 Enter 键。此外,虽然 termios 在 *Linux* 上可能很常见,但它不是 *C 标准库* 的一部分。 (2认同)

Ram*_*uri 39

你也可以这样做:

fseek(stdin,0,SEEK_END);
Run Code Online (Sandbox Code Playgroud)

  • @EvAlex:这有很多问题.如果它适用于您的系统,那很好; 如果没有,那么这并不奇怪,因为当标准输入是交互式设备(或不可搜索的设备,如管道或套接字或FIFO)时,没有什么能保证它能够工作,仅举几个其他方式可以失败). (5认同)
  • 我不知道,我认为它没有提到它,但是stdin是FILE*而fseek接受FILE*参数.我测试了它,它适用于Mac OS X但不适用于Linux. (2认同)

Dmi*_*tri 11

线条:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
    ;
Run Code Online (Sandbox Code Playgroud)

不会只读取换行符('\n')之前的字符.它会读取流中的所有字符(并丢弃它们),直到并包括下一个换行符(或遇到EOF).为了使测试成立,它必须首先读取换行符; 所以当循环停止时,换行符是最后一个字符读取,但它已被读取.

至于为什么它读取换行而不是回车,这是因为系统已将回报转换为换行符.当按下enter时,表示该行的结束...但该流包含换行,因为这是系统的正常行尾标记.这可能取决于平台.

此外,fflush()在输入流上使用并不适用于所有平台; 例如,它通常不适用于Linux.


M.M*_*M.M 10

清除到您已尝试部分阅读的行尾的便携方式是:

int c;

while ( (c = getchar()) != '\n' && c != EOF ) { }
Run Code Online (Sandbox Code Playgroud)

这会读取并丢弃字符,直到它\n发出信号结束文件为止.EOF如果输入流在行结束之前关闭,它还会进行检查.c必须int(或更大)的类型才能保存该值EOF.

没有可移植的方法来确定当前行之后是否还有更多行(如果没有,getchar则会阻止输入).

  • @AbhishekMane 但是一个字节是 8 位,而 256 需要 9 位。如果将 getchar() 的结果分配给 char,则无法处理任何 8 位值以及一个附加标志值。另外,“stdio.h”有“#define EOF (-1)”。C 有 char、signed char、unsigned char,根据体系结构,char 可能为 0..255 或 -128..127。`man getchar` 非常明确:“fgetc() 从流中读取下一个字符,并将其作为 unsigned char 强制转换为 int 返回,或者在文件末尾或发生错误时返回 EOF。” 所以 (int) -1 或 (int) 0..255。将其填充到 char 变量中会丢弃重要信息。 (2认同)

zwo*_*wol 8

但我无法解释自己是如何运作的?因为在while语句中,我们使用getchar() != '\n',这意味着读取任何单个字符,除了'\n'?? 如果是这样,在输入缓冲区仍然是'\n'字符??? 我误解了什么吗?

您可能没有意识到的是,在从输入缓冲区中删除字符 进行比较getchar().因此,当你到达时'\n',它被消耗,然后你就会脱离循环.


Ste*_*mit 8

scanf是一个奇怪的函数,电影《战争游戏》中的一句经典台词与之相关:“唯一获胜的举动就是不玩”。

\n

如果您发现自己需要“刷新输入”,那么您已经输了。获胜之举不是拼命寻找某种神奇的方法来刷新令人烦恼的输入:相反,您需要做的是以某种不同(更好)的方式进行输入,这不涉及在输入流上留下未读的输入,并将其放在那里并导致问题,因此您必须尝试冲洗它。

\n

基本上有三种情况:

\n
    \n
  1. 您正在使用 读取输入scanf,并且它将用户的换行符留在输入流上,并且稍后调用 或 会错误地读取该杂散getchar换行符fgets。(这就是您最初询问的情况。)

    \n
  2. \n
  3. 您正在使用 读取输入scanf,并且它将用户的换行符留在输入流上,并且稍后调用 时会错误地读取该杂散换行符scanf("%c")

    \n
  4. \n
  5. 您正在使用 读取数字输入scanf,并且用户正在键入非数字文本,并且非数字文本留在输入流上,这意味着下一次调用也会scanf失败。

    \n
  6. \n
\n

在所有这三种情况下,似乎正确的做法是“刷新”有问题的输入。你可以尝试,但往好里说是很麻烦,往坏了说是不可能的。最后,我认为尝试刷新输入是错误的方法,并且有更好的方法,具体取决于您担心的情况:

\n

对于情况 1,更好的解决方案是,不要将scanf与其他输入函数的调用混合在一起。要么用 进行所有输入,要么用and/orscanf进行所有输入。要使用 进行所有输入,您可以将调用替换为\xe2\x80\x94,但请参阅第 2 点。理论上,您可以将调用替换为,尽管这会带来各种进一步的问题,我不建议这样做。即使您想要/需要一些\ 的解析,也要完成所有输入,您可以使用 读取行,然后使用 来解析它们。getcharfgetsscanfgetcharscanf("%c")fgetsscanf("%[^\\n]%*c")fgetsscanffgetssscanf

\n

对于情况 2,更好的解决方案是永远不要使用scanf("%c"). 使用scanf(" %c")代替神奇的额外空间让一切变得不同。(对于这个额外空间的作用以及它为什么有帮助有很长的解释,但这超出了这个答案的范围。)

\n

而对于情况3,恐怕根本就没有什么好的解决办法。 scanf有很多问题,其中之一就是它的错误处理很糟糕。如果您想编写一个读取数字输入的简单程序,并且可以假设用户在提示时始终会键入正确的数字输入,那么scanf("%d")可以是足够的 \xe2\x80\x94\xc2\xa0 勉强足够的 \xe2\ x80\x94 输入法。但也许你的目标是做得更好。也许您想提示用户输入一些数字,并检查用户是否确实输入了数字,如果没有,则打印一条错误消息并要求用户重试。在这种情况下,我相信无论出于何种意图和目的,你都无法基于 来实现这一目标scanf。你可以尝试,但这就像给蠕动的婴儿穿上连体衣:将双腿和一只手臂放进去后,当你试图将另一只手臂放进去时,一条腿会蠕动出来。尝试使用 来读取和验证可能不正确的数字输入的工作量太大了scanf。以其他方式做到这一点要容易得多。

\n

您会注意到贯穿我列出的所有三个案例的主题:它们都以“您正在使用scanf...读取输入”开头。这里有一个不可避免的结论。请参阅另一个问题:What can I use for input conversionrather not scanf?

\n

现在,我意识到我仍然没有回答您实际提出的问题。当人们问“我该怎么做 X?”时,如果所有的答案都是“你不应该做 X”,那真的会令人沮丧。如果您真的非常想刷新输入,那么除了人们在这里给您的其他答案之外,另外两个充满相关答案的好问题是:

\n\n


kap*_*pil 6

你可以试试

scanf("%c%*c", &ch1);
Run Code Online (Sandbox Code Playgroud)

其中%*c接受并忽略换行符

再一个方法,而不是fflush(stdin),它调用你可以写的未定义的行为

while((getchar())!='\n');
Run Code Online (Sandbox Code Playgroud)

不要忘记while循环后的分号

  • 1)`"%*c"`扫描任何字符(并不保存),无论是换行还是别的.代码依赖于第二个字符是一个新行.2)`while((getchar())!='\n');`是一个无限循环应该``getchar()`返回`EOF`归因于文件结束. (2认同)

归档时间:

查看次数:

318466 次

最近记录:

7 年,2 月 前