C 语言控制台程序清除输入缓冲区

编写 C 语言控制台程序时,如果使用 scanf() 函数作为输入,往往需要解决非法输入的问题。比如以下情况:

1
2
3
printf("输入一个0~9的整数a\n");
int a;
scanf("%d", &a);

此时可能出现如下几种典型的非法输入:

  • 123
  • 123xyz
  • xyz123

对于第一种,使用 if 判断 a 的范围是否满足要求即可。第二种和第三种则较为复杂,因为其中涉及到了字符的输入。第二种情况,scanf() 会将 123 存入 a,而 xyz 留在了输入缓冲区;第三种情况,scanf() 不会读取任何数据,xyz123 都留在了输入缓冲区。
如果只是判断这一次输入的合法性,可以利用 scanf() 的返回值。如果输入有效,scanf() 会返回 1,否则返回 0。由此可以排除 scanf() 无法读取的输入。

然而,仅仅这样做是不够的。假设之后继续运行如下代码:

1
2
3
printf("输入一个整数b\n");
int b;
scanf("%d", &b);

如果在输入 a 时,非法输入的 xyz 或 xyz123 留在了输入缓冲区,那么当 scanf() 再次执行时,并不会直接读取你第二次输入的数据,而是从上一次输入在输入缓冲区留下的数据开始。即使你在输入 b 时给出了合法的输入,也不会被正确读取。同理,使用 getchar() 得到的也是缓冲区中的第一个字符。
所以,问题的本质是:如果有多次输入,需要在每次输入之前,清除输入缓冲区。否则,之前由于非法输入导致留在输入缓冲区中的数据,会直接影响到这次输入。
而解决办法,通过查找各类论坛,总结出来有如下几种:

  • setbuf(stdin, NULL)
  • fflush(stdin)
  • scanf("%*[^\n]%*c")
  • rewind(stdin)

其原理各不相同,但经过测试,像 fflush(stdin) 这样的函数,在 VC 中可以清除输入缓冲区,但在 XCode(或 gcc)中不起作用。而 scanf("%*[^\n]%*c") 原理是读取输入缓冲区的所有字符,达到清除输入缓冲区的目的;这似乎也不能解决某些形式的非法输入。
经研究,最为有效的办法是在每次接受输入前加一句 rewind(stdin),其效果是坠吼的。