首页 > 解决方案 > scanf 失败后输入流中剩余的内容

问题描述

几年前我使用 K&R 第 2 版 ANSI C 学习了 C。我一直在复习我的笔记,同时我正在从其他 2 本书中学习更现代的 C。

我注意到 K&R 在书中从不使用 scanf,除了在他们介绍它的一节中。他们主要使用他们在书中编写的 getline 函数,一旦他们引入了指针,他们将在本书后面进行更改。getline 与 gcc getline 不同,这给我带来了一些问题,直到我将 getline 的名称更改为 ggetline。

回顾我的笔记,我发现了这句话:

这种简化很方便,而且表面上很有吸引力,而且它确实有效。问题是 scanf 在更复杂的情况下不能很好地工作。在 7.1 节中,我们说过对 putchar 和 printf 的调用可以交错。scanf 的情况并非总是如此:如果您尝试混合对 scanf 的调用与对 getchar 或 getline 的调用,则会遇到令人费解的问题。更糟糕的是,事实证明,scanf 的错误处理对于许多用途来说是不够的。它会告诉您转换是否成功(更准确地说,它会告诉您成功的转换次数),但它不会告诉您更多信息(除非您非常仔细地询问)。与 atoi 和 atof 一样,scanf 在处理 %d 或 %f 输入并找到非数字字符时会停止读取字符。假设你' 已经提示用户输入一个数字,而用户不小心输入了字母“x”。scanf 可能返回 0,表示它无法转换数字,但不可转换的文本(“x”)仍保留在输入流中,除非您找到其他方法将其删除。

由于这些原因(以及其他几个原因,我不会费心提及),通常建议不要将 scanf 用于非结构化输入,例如用户提示。最好用 getline 之类的东西读取整行(正如我们一直在做的那样),然后以某种方式处理该行。如果该行应该是单个数字,则可以使用 atoi 或 atof 对其进行转换。如果该行具有更复杂的结构,您可以使用 sscanf (我们稍后会遇到)来解析它。(使用 sscanf 比使用 scanf 更好,因为当 sscanf 失败时,您可以完全控制接下来要做什么。另一方面,当 scanf 失败时,您将受到输入流中它离开您的位置的支配。)

起初我以为这句话来自 K&R,但我在书中找不到。然后我意识到那是我上网的讲义,对于几年前使用 K&R 书籍教授课程的人。

演讲笔记

我知道 K&R 的书现在已经 30 年了,所以在某些方面它已经过时了。

这句话很老了,所以我想知道 scanf 是否仍然有这种行为或改变了?

scanf 失败时是否仍将内容留在输入流中?例如上面:

假设您提示用户输入一个数字,而用户不小心输入了字母“x”。scanf 可能返回 0,表示它无法转换数字,但不可转换的文本(“x”)仍保留在输入流中。

以下是否仍然正确?

putchar 和 printf 可以交错。scanf 的情况并非总是如此:如果您尝试混合对 scanf 的调用与对 getchar 或 getline 的调用,则会遇到令人费解的问题。

自从写了上面的引号以来,scanf 有很大的变化吗?或者它们今天仍然是真实的?

我问的原因是,在我正在阅读的新书中,没有人提到这些问题。

标签: cscanf

解决方案


scanf()是邪恶的 - 使用fgets()然后解析。


细节并不是scanf()完全糟糕。

1)格式说明符经常以弱的方式使用

char buf[100];
scanf("%s", buf); // bad - no width limit

2) 错误地不检查返回值

scanf("%99[\n]", buf); // what if use entered `"\n"`?
puts(buf); 

3) 当输入与预期不符时,不清楚stdin.

if (scanf("%d %d %d", &i, &j, &k) != 3) {
  // OK, not what is in `stdin`?
}

如果您尝试将 scanf 调用与 getchar 或 getline 调用混合使用,您可能会遇到令人费解的问题。

是的。许多scanf()调用会留下尾随'\n'stdin然后由 . 读取为空行getline(), fgets()scanf()不是为了阅读线getline()并且fgets()更适合阅读一行


自从写了上面的引号以来,scanf 有很大的变化吗?

只有这么多的变化才能发生而不会弄乱代码库。@乔纳森莱弗勒

scanf()仍然很麻烦。 scanf()无法接受参数(在格式之后)以指示要接受多少个字符到char *目的地。

一些系统添加了额外的格式选项来提供帮助。

一个基本问题是这样的:

用户输入是邪恶的。将文本输入作为一个步骤,限定输入,然后解析和评估其成功比尝试在一个函数中完成所有这些更健壮。


安全

代码差的弱点scanf()和编码器倾向scanf()一直是黑客的金矿。

IMO,C 缺乏强大的用户输入功能集。


推荐阅读