请看一下这段代码:
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]
的{{1}}失败,b
的{{1}}失败,book
+ a
\0
line1
}。
然后%9[^\n]
解析剩余的行,并在book
处解析刚刚解析的\0
+ line2
。
请注意2点:
\0
附加在其末尾,因为%[]
是字符串的转换说明符。 %9[a]
b
失败时, scanf
未退出。它只是继续扫描进一步的输入。 现在输入book
,%9[a]
b
的{{1}}失败,而book
\0
只会line1
%9[^\n]
被解析了。
然后book
应该解析剩下的行,并且应该在\0
处刚刚解析line2
+ scanf
。
现在,让我们看看究竟发生了什么:
返回值为0表示scanf
没有为任何变量赋值。只需退出line2
而不指定任何值。所以line1
的垃圾数据。在NULL
的情况下,垃圾数据碰巧是scanf
个字符。
但这很奇怪!不是吗?
我的意思是%[...]
退出,如果scanf
在输入的第一个字符处失败。 (即使%[...]
声明中有其他转换说明符。)
但是,如果相同的scanf
在第一个字符以外的任何其他字符处失败,那么scanf
只会继续扫描其他输入。 (如果当然有额外的转换说明符。)它不会退出。
那为什么会出现这种不一致?
为什么不让%[...]
语句继续扫描输入(如果当然还有其他转换说明符),即使$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3
在第一个输入字符处失败?完全像在其他情况下发生的事情
这种不一致背后有什么特殊原因吗?
{{1}}
答案 0 :(得分:5)
2)当
%9[a]
在b失败时,scanf
没有退出。它只是继续扫描进一步的输入。
是的,%9[a]
指令意味着“存储最多 9 'a'
,但至少一个”(1) ,所以转换%9[a]
没有失败,它成功了。它发现'a'
的消耗量比消耗的少'b'
,但这不是失败。输入匹配在[
处失败,但转换成功。
(1)在7.21.6.2(12)中指定转换的描述:
%9[a]
匹配一组预期字符( scanset )中非空序列的字符。
现在对于输入书,
'\0'
应该从书中失败,并且应该只在{1}}存储在第1行,因为这里没有解析任何内容。然后%9[^\n]
应该解析剩余的行,并且应该刚刚在第2行存储解析的book+\0
。
没有。它应该在转换失败时退出。第一次转换%9[a]
失败,因此scanf
应该停止并返回0,因为没有转换成功。
始终检查scanf
的返回值。
在7.21.6.2(16)中指定了(fscanf
,但scanf
相当于fscanf
且stdin
为输入流):
如果发生输入失败,
fscanf
函数将返回宏EOF
的值 在第一次转换(如果有)之前完成。 否则,函数返回 分配的输入项目数量,可以少于提供的数量,甚至为零 早期匹配失败的事件。这里
line1
的输出并不是我们所期望的。一个空字符串!
你不能指望任何事情。数组line1
和line2
未初始化,因此当转换失败时,其内容仍然是不确定的。在这种情况下,line1
在前0字节之前不包含可打印字符。
但对于
line2
来说,它是垃圾字符!我们没想到这一点。那怎么发生呢?
这就是line2
的内容。从来没有为元素分配任何值,因此它们是在调用scanf
之前发生的任何事情。
答案 1 :(得分:1)
从评论转移到问题,因为对回复问题的回复需要比评论允许的更多空间。
此评论涉及代码的早期版本:
由于你没有检查
scanf()
的返回值,你不知道它是否说“我失败了”。当你忽略它的错误返回时,你不能责怪它;在第二个例子中,它会说'0项成功扫描',这意味着没有任何变量被设置为任何有用的东西。您必须始终检查scanf()
的返回值,以便了解它是否符合预期。
回复问题是:
我更新了代码并输出以显示
scanf
的返回值。对于案例2,是的,返回值为0.但这不回答问题。显然,scanf
在案例2中退出。但对于案例1,返回值为2,这意味着scanf
成功地为这两个变量赋值。那么为什么会出现这种不一致呢?
我没有看到任何不一致。 fscanf()
规范(从ISO / IEC 9899:2011复制,但URL链接到POSIX而不是C标准)说:
¶3[...]每个转换规范都由字符
%
引入。 在%
之后,以下顺序出现:- 可选的赋值抑制字符* - 大于零的可选十进制整数,指定最大字段宽度 (字符)。
- 可选的长度修饰符,用于指定接收对象的大小 - 转换说明符字符,用于指定要应用的转换类型。
后来,它说:
¶8[...]跳过输入空白字符(由
isspace
函数指定),除非 该规范包括[
,c
或n
说明符。 284)¶9除非规范包含
n
说明符,否则将从流中读取输入项。一个 输入项被定义为输入字符的最长序列,不超过 任何指定的字段宽度,它是匹配输入序列的前缀,或者是匹配输入序列的前缀。 285) 输入项目之后的第一个字符(如果有)仍未读取。 如果输入的长度 item为零,指令的执行失败 ;这种情况是匹配失败,除非 文件结束,编码错误或读取错误阻止了流的输入,其中 如果是输入失败。¶12[...]
[
匹配一组预期字符中的 非空 字符序列 (扫描集)。 286)
[加粗斜体重点。我已经留下了脚注参考资料,但脚注的内容对讨论来说并不重要,所以我省略了它们。]
因此,您所看到的行为正是标准所要求的。当%9[a]
应用于字符串abook
时,会有一个a
的序列与%9[a]
转换规范匹配,因此该指令成功,并继续扫描book
。当%9[a]
应用于字符串book
时,与该项匹配的零字符,因此指令的执行失败并且它是匹配的错误,因为它是第一个转换规范,返回值0是正确的。
请注意,长度指定了最大字段宽度,因此9
中的%9[a]
表示1-9个字母a
。