c - 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 有很大的变化吗?或者它们今天仍然是真实的?
我问的原因是,在我正在阅读的新书中,没有人提到这些问题。
解决方案
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 缺乏强大的用户输入功能集。
推荐阅读
- c++ - 为什么我不能将左值引用绑定到右值,而概念可以?
- java - 将 springboot java RestHighLevelClient 连接到 aws elasticsearch
- flutter - 更改颜色 ListView.Builder onPressed Flutter
- c++ - 为什么我不能得到初始化列表提供的自定义概念的编译错误?
- x86-64 - 无法在真实硬件上设置视频模式,OSDEV
- mysql - 如何更新 3 个表 1 个查询
- git - 将 reflog 重置为当前 HEAD 位置的最快方法?
- gridsearchcv - GridSearchCV 找到最佳正则化参数:如何访问验证分数和训练分数
- flutter - Streambuilder 中的 Listviewer 无法正确更新已删除的对象
- django - 如何使用 Django 列表视图遍历 Chart.js