为了回应我在another answer about C++ 中发布的内容:我建议将scanf() 扔掉,永远不要使用它,而是使用fgets() 和sscanf()。
这样做的原因是,至少在默认情况下,在类 Unix 系统中,运行 CLI 程序的终端会在您的程序看到用户输入之前对其进行一些处理。它会缓冲输入,直到输入换行符,并允许进行一些基本的行编辑,例如使退格起作用。
因此,您永远不能一次获取一个字符,或者几个单个字符,而只能获取一整行。但这不是例如scanf("%d") 处理,而不是只处理数字,并停在那里,将其余部分缓存在 C 库中,以供将来的 stdio 函数使用。如果你的程序有例如
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
并且您输入123 abcd 行,它同时完成both scanf()s,但只有在给出换行符之后。第一个scanf() 不会在用户点击空格时返回,即使那是数字结束的地方(因为此时该行仍在终端的行缓冲区中);而第二个scanf() 不会等待您输入另一行(因为输入缓冲区已经包含足够的内容来填充%s 转换)。
这不是用户通常期望的!
相反,他们希望按回车键完成输入,如果按回车键,您会得到一个默认值或一个错误,可能会建议您真正给出答案。
scanf("%d") 无法真正做到这一点。如果用户只是按 Enter 键,则什么也不会发生。因为scanf()还在等号码。终端继续发送该行,但您的程序看不到它,因为scanf() 吃掉了它。您没有机会对用户的错误做出反应。
这也不是很有用。
因此,我建议使用fgets() 或getline() 一次读取整行输入。这与终端给出的完全匹配,并且总是在用户输入一行后给予您的程序控制权。你对输入行做什么取决于你,如果你想要一个数字,你可以使用atoi(),strtol(),甚至sscanf(buf, "%d", &a)来解析数字。 sscanf() 与 scanf() 没有相同的不匹配,因为它读取的缓冲区大小有限,当它结束时,它就结束了——函数不能等待更多。
(fscanf() 在常规文件上也可以,如果文件格式支持它如何像任何空格一样跳过换行符。对于面向行的数据,我仍然会使用 fgets() 和sscanf().)
所以,不要使用我上面的内容,而是使用如下内容:
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
或者,实际上,也检查sscanf() 的返回值,这样您就可以检测空行和其他无效数据:
#include <stdio.h>
int main(void)
{
const int bufsize = 100;
char buf[bufsize];
int a;
int ret;
char word[bufsize];
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%d", &a);
if (ret != 1) {
fprintf(stderr, "Ok, you don't have to.\n");
return 1;
}
printf("Enter a word: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%s", word);
if (ret != 1) {
fprintf(stderr, "You make me sad.\n");
return 1;
}
printf("You entered %d and %s\n", a, word);
}
当然,如果你想让程序坚持,你可以创建一个简单的函数来循环fgets() 和sscanf(),直到用户愿意按照他们的指示去做;或立即退出并出现错误。取决于您认为如果用户不想打球,您的程序应该做什么。
你可以做类似的事情,例如通过循环 getchar() 来读取字符,直到 scanf("%d") 返回后的换行符,从而清除缓冲区中剩余的任何垃圾,但这对于用户只是在空行上输入的情况没有任何作用。无论如何,fgets() 会一直读到换行符,所以你不必自己做。