另一个关于 C++ 的答案 中发布的内容 :我建议扔掉 scanf()
它,永远不要使用它,而是使用 fgets()
和 sscanf()
.
原因在于,至少在类 Unix 系统中,默认情况下,CLI 程序运行的终端会在程序看到用户输入之前对其进行一些处理。它会缓冲输入,直到输入换行符,并允许进行一些基本的行编辑,例如使退格键起作用。
因此,您永远无法一次获得单个字符,或几个单个字符,而只能获得整行。但这不是 eg scanf("%d")
处理的内容,相反,它只处理数字, 然后就此停止 ,其余部分则缓冲在 C 库中,以供将来的 stdio
函数使用。如果您的程序有 eg
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
并且您输入行 123 abcd
,它会 同时 scanf()
两个 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")
返回后出现换行符,从而清除缓冲区中剩余的任何垃圾,但这对用户在空行上按 Enter 键的情况没有任何作用。无论如何, fgets()
都会读取到换行符,因此您不必自己执行此操作。