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

时间:2011-10-26 02:48:18

标签: 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;
}

正如上述代码的作者所解释的那样: 该程序将无法正常工作,因为在第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' );
    }
    
  3. 此代码实际上有效。但我无法解释自己它是如何运作的?因为在while语句中,我们使用getchar() != '\n',这意味着读取除'\n'之外的任何单个字符?如果是这样,在输入缓冲区中仍然是'\n'字符?

14 个答案:

答案 0 :(得分:74)

  

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

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

如果您输入了一个字符并按了Enter键,则第1行将读取该输入字符,然后第2行将读取'\n'。请参阅comp.lang.c中的I'm using scanf %c to read a Y/N response, but later input gets skipped.。常见问题。

关于建议的解决方案,请参阅(再次从comp.lang.c FAQ):

基本上说明了唯一的便携式方法:

int c;
while ((c = getchar()) != '\n' && c != EOF) { }

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

此外,我觉得有义务阻止您完全使用scanfWhy does everyone say not to use scanf? What should I use instead?

答案 1 :(得分:37)

你也可以这样做:

fseek(stdin,0,SEEK_END);

答案 2 :(得分:11)

行:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
    ;

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

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

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

答案 3 :(得分:10)

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

int c;

while ( (c = getchar()) != '\n' && c != EOF ) { }

这将读取并丢弃字符,直到它获得\n信号结束文件。如果输入流在行结束之前关闭,它还会检查EOFc的类型必须为int(或更大),才能保留值EOF

没有可移植的方法来确定当前行之后是否还有其他行(如果没有,则getchar将阻止输入)。

答案 4 :(得分:8)

  

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

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

答案 5 :(得分:6)

你可以尝试

scanf("%c%*c", &ch1);

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

还有一种方法 而不是调用未定义行为的fflush(stdin),你可以编写

while((getchar())!='\n');

不要忘记while循环后的分号

答案 6 :(得分:4)

尚未提及的另一个解决方案是使用: 倒带(标准输入);

答案 7 :(得分:1)

unsigned char a=0;
if(kbhit()){
    a=getch();
    while(kbhit())
        getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;

-OR -

使用可能使用_getch_nolock() .. ???

答案 8 :(得分:1)

我在尝试实施解决方案时遇到问题

while ((c = getchar()) != '\n' && c != EOF) { }

我发布了一些调整&#39;代码B&#39;对于任何可能遇到同样问题的人。

问题在于该计划让我抓住了这个&#39; \ n&#39;字符,独立于输入字符,这是给我问题的代码。

代码A

int y;

printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
   continue;
}
printf("\n%c\n", toupper(y));

调整是为了抓住&#39;在评估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循环,它必须分配值&#39; \ n&#39;到变量y,所以它将是最后指定的值。

如果我错过了解释的内容,代码A或代码B,请告诉我,我几乎不是新的。

希望它可以帮助某人

答案 9 :(得分:0)

简短,可移植并在stdio.h中声明

stdin = freopen(NULL,"r",stdin);

当stdin上没有任何东西可以像下面这个众所周知的行一样冲洗时,不会挂在无限循环中:

while ((c = getchar()) != '\n' && c != EOF) { }

有点贵,所以不要在需要反复清除缓冲区的程序中使用它。

从同事那里偷走了:))

答案 10 :(得分:0)

我很惊讶没有人提及此事

scanf("%*[^\n]");

答案 11 :(得分:0)

简而言之。把线...

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 的回答是最全面的。

答案 12 :(得分:-1)

char choice;
do{
    printf("\n *");
    printf("\n Do you want to continue? (y/n) ");
    choice = getchar();
    if(getchar() != '\n'){
        fflush(stdin);
    }
}while( choice != 'n' );

答案 13 :(得分:-1)

尝试一下:

stdin->_IO_read_ptr = stdin->_IO_read_end;