几年前,我使用 K&R 第二版 ANSI C 学习了 C。我\xe2\x80\x99 一直在复习我的笔记,同时我\xe2\x80\x99 正在从其他两本书中学习更现代的 C。
\n\n我注意到 K&R 在书中从未使用过 scanf,除了他们介绍它的一节。他们主要使用他们在书中编写的 getline 函数,一旦引入指针,他们就会在书中稍后更改该函数。getline 与 gcc getline 不同,这给我带来了一些问题,直到我将 getline 的名称更改为 ggetline。
\n\n回顾我的笔记,我发现了这句话:
\n\n\n\n\n这种简化很方便,而且表面上很有吸引力,而且就目前而言它是有效的。问题是 scanf 在更复杂的情况下不能很好地工作。在第 7.1 节中,我们说过对 putchar 和 printf 的调用可以交错。对于 scanf 来说,情况并不总是如此:如果尝试将对 scanf 的调用与对 getchar 或 getline 的调用混合在一起,则可能会遇到令人困惑的问题。更糟糕的是,事实证明 scanf 的错误处理对于许多用途来说是不够的。它告诉您转换是否成功(更准确地说,它告诉您有多少转换成功),但它不会告诉您更多信息(除非您非常仔细地询问)。与 atoi 和 atof 一样,scanf\n 在处理 %d 或 %f 输入并且发现非数字字符时停止读取字符。假设您提示用户输入数字,而用户不小心键入了字母“x”。scanf\n 可能会返回 0,表明它无法转换数字,但不可转换的文本(\'x\')仍保留在输入流中,除非您想出其他方法来删除它。
\n\n由于这些原因(以及其他几个原因,我不会费心提及),通常建议不要将 scanf 用于非结构化输入,例如用户提示。最好使用 getline 之类的东西读取整行(正如我们一直在做的那样),然后以某种方式处理该行。如果该行应该是单个数字,则可以使用 atoi 或 atof 对其进行转换。如果该行的结构更复杂,您可以使用 sscanf(我们稍后会介绍)来解析它。(使用 sscanf 比使用 scanf 更好,因为当 sscanf 失败时,您可以完全控制下一步要做什么。另一方面,当 scanf 失败时,您将受到以下位置的支配:在输入流中它已经离开了你。)
\n
起初我以为这句话来自K&R,但我在书中找不到它。然后我意识到这是我在网上找到的讲义,是几年前使用 K&R 书教授课程的人的讲义。
\n\n\n\n我知道 K&R 的书已经有 30 年历史了,所以它在某些方面已经过时了。
\n\n这句话很旧了,所以我想知道 scanf 是否仍然有这种行为或者已经改变了?
\n\nscanf 失败时是否仍会在输入流中留下内容?例如上面的例子:
\n\n\n\n\n假设您已提示用户输入数字,而用户不小心键入了字母“x”。scanf 可能会返回 0,表明它无法转换数字,但不可转换的文本(\'x\')仍保留在输入流中。
\n
以下内容仍然正确吗?
\n\n\n\n\nputchar 和 printf 可以交错。对于 scanf 来说,情况并不总是如此:如果尝试将对 scanf 的调用与对 getchar 或 getline 的调用混合在一起,则可能会遇到令人困惑的问题。
\n
自从写完上面的引文以来,scanf 发生了很大的变化吗?或者它们在今天仍然是正确的吗?
\n\n我之所以问,是因为在我读的新书中,没有人提到这些问题。
\nscanf()是邪恶的——使用fgets()然后解析。
细节并不是scanf()完全糟糕。
1)格式说明符通常以弱方式使用
char buf[100];
scanf("%s", buf); // bad - no width limit
Run Code Online (Sandbox Code Playgroud)
2)错误地未检查返回值
scanf("%99[\n]", buf); // what if use entered `"\n"`?
puts(buf);
Run Code Online (Sandbox Code Playgroud)
3)当输入不符合预期时,不清楚 中还剩下什么stdin。
if (scanf("%d %d %d", &i, &j, &k) != 3) {
// OK, not what is in `stdin`?
}
Run Code Online (Sandbox Code Playgroud)
如果您尝试将对 scanf 的调用与对 getchar 或 getline 的调用混合在一起,您可能会遇到令人困惑的问题。
是的。许多scanf()调用都会留下一个尾随'\n',stdin然后被 读取为空行getline(), fgets()。 scanf()不适合阅读行。 getline()并且fgets()更适合阅读一行。
自从写完上面的引文以来,scanf 发生了很大的变化吗?
在不搞乱代码库的情况下,只能进行如此多的更改。@乔纳森莱夫勒
scanf()仍然很麻烦。 scanf()无法接受参数(在格式之后)来指示目标接受多少个字符char *。
有些系统添加了额外的格式选项来提供帮助。
一个基本问题是:
用户输入是邪恶的。与尝试在一个函数中完成所有这些操作相比,一步获得文本输入、限定输入、然后解析和评估其成功程度更为稳健。
安全
scanf()编码质量差的弱点和编码员的倾向scanf()一直是黑客的金矿。
IMO,C 缺乏强大的用户输入功能集。