[英]What is the difference between these two scanf statements?
我有些怀疑。 疑问是
以下两个scanf语句之间有什么区别?
scanf("%s",buf);
scanf("%[^\n]", buf);
如果我在while循环中提供第二个scanf,它将无限进行。 因为\\n
在stdin
。
但是在第一条语句中,读取的内容是\\n
之前的内容。 它也不会读取\\n
。
但是第一条陈述并不是无限的。 为什么?
关于的特性%s
格式说明,引用C11
standrad,章§7.21.6.2, fscanf()
s
匹配一系列非空格字符。
换行符是一个空格字符,因此只有newlinew
不会与%s
匹配。
因此,如果换行符留在缓冲区中,则它不会单独扫描换行符,而是等待下一个非空白输入出现在stdin
。
%s
格式说明符指定scanf()
应该读取标准输入缓冲区stdin
中的所有字符,直到遇到第一个空格字符,然后在此停止。 空格( '\\n'
)保留在stdin
缓冲区中,直到被另一个函数(如getchar()
占用。
在第二种情况下,没有提及停止。
您可以将scanf视为从字符流中提取由空格分隔的单词。 想象一下,例如,读取一个包含数字表的文件,而不必担心每行的确切数字计数或数字之间的确切空间计数和性质。
记录的空白是水平和垂直(存在)选项卡,回车符,换行符,换页符以及最后同样重要的是实际空间。
为了使用户摆脱细节,scanf对待所有空格都相同:通常跳过它,直到遇到非空格,然后根据指定的输入转换尝试从此处开始转换字符序列。 例如,使用“%d”,它需要一个数字序列,也许前面带有一个负号。
输入转换“%s”也从跳过空格开始(与Linux相比, opengroup的手册页中记录的空格更清楚)。
跳过前导空格后,“%s”将接受所有内容,直到读取了另一个空格(并放回输入中,因为它没有成为要读取的“单词”的一部分)。 非空白字符的序列(基本上是一个“单词”)存储在提供的缓冲区中。 例如,从" a bc "
扫描字符串会导致跳过3个空格并将“ a”存储在缓冲区中。 (下一个scanf将跳过中间空间,并将“ bc”放入缓冲区。此后的下一个scanf将跳过剩余的空白,遇到文件末尾并返回EOF。)因此,如果要求用户输入三个单词,则他们可以在一行或三行或在以空行开头或分隔的任何数目的行(即任何数目的后续换行)上给出三个单词。 Scanf一点也不在乎。
“跳过领先空白”策略有一些例外。 两者都涉及转换,这些转换通常表明用户希望对输入转换有更多的控制。 其中之一是“%c”,它仅读取下一个字符。 另一个是“%[”规范,该规范详细说明了哪些字符被视为下一个要读取的“单词”的一部分。 您使用的转换规范“%[^ \\ n]”读取除换行符以外的所有内容。 通常,从键盘输入的内容会逐行传递给程序,根据定义,每一行都以换行符结尾。 传递给程序的第一行的换行符将是输入流中与转换规范不匹配的第一个字符。 Scanf将读取它,检查它,然后将其放回输入流(使用ungetc()
),以供其他人使用。 不幸的是,它本身将是另一个循环迭代中的下一个消费者(如我所假设的)。 现在它遇到的第一个字符(新行)不匹配输入转换(它要求什么,但换行)。 因此,Scanf立即放弃,将有问题的字符忠实地放回输入中,以供其他人使用,并返回0,这表示无法执行格式字符串中的第一个转换。 但是可惜,它本身将是下一个消费者。 是的,机器很蠢。
第一个scanf(“%s”,buf); 仅扫描单词或字符串,但仅扫描第二个scanf(“%[^ \\ n]”,buf); 读取字符串,直到用户输入的换行符为止。
您可以将%s
视为%[^\\n \\t\\f\\r\\v]
,也就是说,跳过任何前导空格后,将一个非空格字符组。
让我们看一下这两个代码片段:
#include <stdio.h>
int main(void){
char sentence[20] = {'\0'};
scanf("%s", sentence);
printf("\n%s\n", sentence);
return 0;
}
输入:您好,我叫Claudio。
输出:你好
#include <stdio.h>
int main(void){
char sentence[20] = {'\0'};
scanf("%[^\n]", sentence);
printf("\n%s\n", sentence);
return 0;
}
输入:您好,我叫Claudio。
输出:您好,我叫Claudio。
%[^\\n]
是反向组扫描,这就是我个人使用的方式,因为它允许我输入带有空格的句子。
共同
两者都希望buf
是指向字符数组的指针。 如果保存了至少1个字符,则两者都会在该数组后添加一个空字符。 如果保存了某些内容,则两者都返回1。 如果在保存任何内容之前检测到文件结束,则两者都将返回EOF
。 两者都检测到输入错误返回EOF
。 两者都可以保存带有嵌入式'\\0'
字符的buf
。
scanf("%s",buf);
scanf("%[^\n]", buf);
差异
"%s"
1)消耗和丢弃包括'\\n'
,空格,制表符等在内'\\n'
前导空白。2)然后将非空白保存为buf
直到3)检测到空白(然后将其放入回到stdin
)。 buf
将不包含任何空格。
"%[^\\n]"
1) 不消耗和废弃前导空白。 2)将非'\\n'
字符保存到buf
直到3)检测到'\\n'
(然后将其放回stdin
)。 如果读取的第一个字符是'\\n'
,则buf
中将不保存任何内容 ,并返回0。 '\\n'
保留在stdin
并说明OP的无限循环。
无法测试scanf()
的返回值是常见的代码疏忽。 更好的代码检查scanf()
的返回值。
IMO:代码不得使用以下任何一种方式:
两者均不能限制读取的字符数。 使用fgets()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.