c - 与 C 中 scanf() 中的 [ ] 转换说明符的逻辑不一致

标签 c format-specifiers

请看一下这段代码:

char line1[10], line2[10];
int rtn;
rtn = scanf("%9[a]%9[^\n]", line1, line2);
printf("line1 = %s|\nline2 = %s|\n", line1, line2);
printf("rtn = %d\n", rtn);

输出:

$ gcc line.c -o line
$ ./line
abook
line1 = a|
line2 = book|
rtn = 2
$./line
book
line1 = |
line2 = �Js�|
rtn = 0
$

对于输入 abook%9[a]b 来自 book 并存储之前解析的a+\0line1
然后 %9[^\n] 解析剩下的行,并将刚刚解析的 book+\0 存储在 line2.
这里请注意两点:

  1. 在存储解析的输入时,\0 附加在它的末尾,因为 %[] 是字符串的转换说明符。
  2. %9[a]b 失败时,scanf 没有退出。它只是继续扫描进一步的输入。

现在对于输入 book%9[a] 应该在 bookb 处失败并且应该仅将 \0 存储在 line1 中,因为此处未解析任何内容。 然后 %9[^\n] 应该解析剩余的行,并且应该将刚刚解析的 book+\0 存储在 line2

现在,让我们看看到底发生了什么:
这里的返回值为 0,表示 scanf 没有给任何变量赋值。 scanf 没有分配任何值就直接退出了。所以 line2 处的垃圾数据。在 line1 的情况下,垃圾数据恰好是一个 NULL 字符。

但这很奇怪!不是吗?
我的意思是,如果 %[...] 在输入的第一个字符处失败,则 scanf 退出。 (即使在 scanf 语句中有额外的转换说明符。)
但是,如果相同的 %[...] 在第一个字符以外的任何其他字符处失败,则 scanf 只是继续扫描进一步的输入。 (当然,如果有额外的转换说明符。)它不会退出。

那么为什么会出现这种不一致呢? 为什么不让 scanf 语句继续扫描输入(当然如果有额外的转换说明符)即使 %[...] 在输入的第一个字符处失败?与其他情况完全一样。
这种不一致背后有什么特殊原因吗?

$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3

最佳答案

2) When %9[a] failed at b, scanf didn't exit. It simply went on scanning further input.

是的,%9[a] 指令意味着“存储最多 9 个'a',但至少one"(1),所以转换 %9[a] 没有失败,它成功了。它发现的 'a' 少于它可以消耗的数量,但这并不是失败。输入匹配'b'处失败,但转换成功。

(1) 在描述转换的 7.21.6.2 (12) 中指定:

[ Matches a nonempty sequence of characters from a set of expected characters (the scanset).

Now for input book, %9[a] should fail at b from the book and should store just '\0' at line1 since here nothing was parsed. Then %9[^\n] should parse the remaining line and should store just now parsed book+\0 at line2.

没有。它应该在转换失败时退出。第一次转换 %9[a] 失败,因此 scanf 应该停止并返回 0,因为没有转换成功。

始终检查scanf 的返回值。

这是指定的(对于fscanf,但是scanf等同于fscanf with stdin作为输入流)在 7.21.6.2 (16) 中:

The fscanf function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.

Here output for line1 is nothing which is exactly what we expected. An empty string!

你不能期待任何事情。数组 line1line2 没有初始化,所以当转换失败时,它们的内容仍然是不确定的。在这种情况下,line1 在第一个 0 字节之前不包含任何可打印字符。

But for line2 it's garbage chars! We didn't expect this. So how did this happen ?

这就是 line2 的内容。从来没有为元素分配任何值,因此它们是调用 scanf 之前的任何值。

关于c - 与 C 中 scanf() 中的 [ ] 转换说明符的逻辑不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16778044/

相关文章:

c - scanf 和 p 转换说明符

c - 为什么使用 "%s"格式说明符 printf 一个字符数组会在数组中的最后一个字符之后输出额外的无意义值?

objective-c - 这个 %1$@%2$d 格式说明符在 objective-c 中意味着什么

c - FFMpeg hello world 失败

c - 我的输出总是在我期望的输出之前打印 '.' 。 CS50 PSET2

c++ - 在 C++ 中使用 C 的各种格式说明符

ios - 格式说明符

c - 创建 : <Method Name> <Num of times called> for a particular project directory list 的脚本

通过 fgets() 读取文件时检查错误

c - 程序在运行时在提供输入时崩溃