这两个 scanf 语句有什么区别?

What is the difference between these two scanf statements?

我有一些疑问。疑点是

下面两个scanf语句有什么区别

scanf("%s",buf);

scanf("%[^\n]", buf);

如果我在 while 循环中给出第二个 scanf,它将无限进行。因为 \nstdin.

但在第一个语句中,读取到 \n 之前。它也不会读取 \n

但是第一个语句并没有无限往里走。为什么?

关于 %s 格式说明符的属性,引用 C11 standrad,章节 §7.21.6.2,fscanf()

s Matches a sequence of non-white-space characters.

换行符是一个空白字符,因此只有 newlinew 不会匹配 %s

因此,如果换行符留在缓冲区中,它不会单独扫描换行符,而是等待下一个non-whitespace输入出现在stdin.

%s 格式说明符指定 scanf() 应读取标准输入缓冲区 stdin 中的所有字符,直到遇到第一个空白字符,然后在那里停止。空格 ('\n') 保留在 stdin 缓冲区中,直到被另一个函数使用,例如 getchar().

第二种情况没有提到停止。

第一个 scanf("%s",buf); 只扫描单词或字符串,第二个 scanf("%[^\n]", buf); 读取一个字符串,直到用户输入换行符。

您可以将 scanf 视为从字符流中提取以白色space 分隔的单词。例如,想象一下读取一个包含 table 个数字的文件,而不用担心每行的确切数字计数或数字之间的确切 space 计数和性质。

Whitespace,为了记录,是水平和垂直(这些存在)制表符、回车符 returns、换行符、换页符,最后一点,实际 spaces .

为了将用户从细节中解放出来,scanf 对所有白色space 都一视同仁:它通常会跳过它,直到遇到 non-whitespace,然后尝试根据从那里开始转换字符序列到指定的输入转换。例如。对于“%d”,它需要一个数字序列,可能前面有一个减号。

输入转换“%s”也从跳过白色开始space(在 opengroup's man page than in the Linux one 中有更清楚的记录)。

跳过前导白色space后,“%s”接受所有内容,直到读取另一个白色space(并放回输入中,因为它不是 "word" 正在阅读)。 non-whitespace 个字符的序列——基本上是一个 "word"——存储在提供的缓冲区中。例如,扫描 " a bc " 中的字符串会导致跳过 3 space 并将 "a" 存储在缓冲区中。 (下一个 scanf 将跳过中间的 space 并将 "bc" 放入缓冲区。之后的下一个 scanf 将跳过剩余的白色 space,遇到文件末尾和 return EOF.) 因此,如果要求用户输入三个单词,他们可以在一行或三行或任意数量的行上输入三个单词,之前或之间由任意数量的空行分隔,即任意数量的后续换行符。 Scanf 一点都不在乎。

"skip leading whitespace" 策略有一些例外。两者都涉及通常表示用户希望对输入转换有更多控制的转换。其中之一是“%c”,它只读取下一个字符。另一个是“%[”规范,它详细说明了哪些字符被认为是下一个要读取的 "word" 的一部分。您使用的转换规范“%[^\n]”读取所有内容 除了 换行符。来自键盘的输入通常逐行传递给程序,每一行根据定义以换行符结束。传递给您的程序的第一行的换行符将是输入流中与转换规范不匹配的第一个字符。 Scanf 将读取它、检查它,然后将它放回输入流(使用 ungetc())供其他人使用。不幸的是,它本身将成为下一个消费者,在另一个循环迭代中(正如我所假设的那样)。现在它遇到的第一个字符(换行符)与输入转换不匹配(需要 换行符)。因此 Scanf 立即放弃,尽职尽责地将有问题的字符放回输入中供其他人使用,并且 returns 0 表示甚至无法执行格式字符串中的第一次转换。但遗憾的是,它本身将成为下一个消费者。是的,机器是愚蠢的。

你可以把%s想象成%[^\n \t\f\r\v],也就是说,在跳过任何前导空格之后,一组non-whitespace个字符。

让我们来看看这两个代码片段:

#include <stdio.h>

int main(void){

        char sentence[20] = {'[=10=]'};

        scanf("%s", sentence);

        printf("\n%s\n", sentence);

return 0;

}

输入:你好,我叫克劳迪奥。

输出:你好

#include <stdio.h>

int main(void){

        char sentence[20] = {'[=11=]'};

        scanf("%[^\n]", sentence);

        printf("\n%s\n", sentence);

return 0;

}

输入:你好,我叫克劳迪奥。

输出:你好,我叫克劳迪奥。

%[^\n] 是一个反向组扫描,这就是我个人使用它的方式,因为它允许我输入一个带有空格的句子。

普通

两者都期望 buf 是指向字符数组的指针。如果至少保存了 1 个字符,则两者都会向该数组附加一个空字符。两者都 return 1 如果有东西被保存了。 return EOF if end-of-file 在保存任何内容之前检测到。检测到 return EOF 输入错误。两者都可以保存 buf,其中嵌入了 '[=15=]' 个字符。

scanf("%s",buf);
scanf("%[^\n]", buf);

差异

"%s" 1) 消耗并丢弃前导 white-space 包括 '\n'、space、制表符等 2) 然后将非 white-space 保存到buf 直到 3) 检测到 white-space(然后将其放回 stdin)。 buf 将不包含任何 white-space.

"%[^\n]" 1) 消耗并丢弃前导white-space。 2) 它将非 '\n' 字符保存到 buf 直到 3) 检测到 '\n'(然后将其放回 stdin)。如果读取的第一个字符是 '\n',则 buf 中不会保存任何内容 并且 return 编辑 0。 '\n' 保留在 stdin 中并解释了 OP 的无限循环。


未能测试 scanf() 的 return 值是一个常见的代码疏忽。更好的代码检查 scanf().

的 return 值

IMO:代码不应该使用:

两者都未能限制读取的字符数。使用 fgets().