scanf("%79[^\n]",line)、scanf("%79[^\n]\n",line) 和 scanf("%79[^\n]s",line) 之间的区别是什么?

3
我正在编写一段用C语言读取文件内容的程序。 代码如下:
   #include<stdio.h>
   void main()
   {
   char line[90];
   while(scanf("%79[^\n]\n",line)==1) 
   printf("%s",line);
   }

以上代码读取文件内容并在屏幕上显示。
但是,
while(scanf("%79[^\n]",line)==1) and while(scanf("%79[^\n]s",line)==1) or while(scanf("%79[^\n]s\n",line)==1) 

不起作用。(它们只显示第一行)

有人可以解释吗?


第一个是接受“字符串”类型输入的。 - Am_I_Helpful
@shekhar suman...它们都接受字符串。 - dividedbyzero
1
%79[^\n] 是格式字符串,不需要后缀s。第二个换行符 %79[^\n]\n 直接跳到下一行。 - David Pullar
2
我认为迷你正则表达式 [^\n] 可以替代 s 格式说明符。如果在行末有一个字面上的 s,则其他格式说明符将匹配。 - Austin Mullins
4个回答

5

格式"%79[^\n]"表示最多79个字符,但不包括'\n'

使用时,

scanf("%79[^\n]s",line)

返回值不应该是1,因为格式说明符中的s在读取所有不是'\n'的字符后,期望接下来的是字面上的s。换句话说,scanf报告失败。

当你使用以下代码:

scanf("%79[^\n]\n",line)

它成功是因为它在末尾找到了一个字面值为'\n'的字符。

两者的区别在于

scanf("%79[^\n]",line)

并且

scanf("%79[^\n]\n",line)

第一种情况是 '\n' 仍然留在输入流中,而在第二种情况下则被消耗。在第二种情况下,不仅 '\n' 被消耗,以 '\n' 开头的任何空格序列也都被消耗了(感谢 @MattMcNabb 的额外澄清)。

如果您想让 scanf 仅消耗 '\n',请使用:

scanf("%79[^\n]%*c",line)

2
请注意,\n 在方括号之外匹配任何空白序列,而不仅仅是单个换行符。此版本还会吞掉以下行的任何前导空格。 - M.M
“返回值不应该是1”这个说法是误导性的。使用scanf(“%79 [^ \ n] s”,line),如果将任何1到79个字符扫描到line中,则scanf()将返回1。匹配或不匹配字面上的“s”并不影响scanf()的结果。 - chux - Reinstate Monica
类似于 scanf("%79[^\n]\n",line) 扫描任何内容到 line 中时,返回值为 1。无论是否找到后面的 "\n",都不会改变 scanf() 的返回值。但是 "\n" 会影响扫描 line 后的空格消耗。 - chux - Reinstate Monica
确定您想要的是 scanf("%79[^\n]*c",line) --> scanf("%79[^\n]%*c",line) - chux - Reinstate Monica
@chux,非常感谢您指出问题。已经修正了答案。您的眼力真棒。 - R Sahu

1

由于格式本身相同,解决这个问题的关键,正如您肯定能告诉的那样,是格式字符串后面的尾随字符(或缺少该字符)。

  • 当您在末尾加上'\n'时,您告诉scanf在读取字符串后读取并忽略行末字符。这很好用,因为'\n'是符合您的格式(即%79[^\n])终止字符串的字符。
  • 当您在末尾加上's'时,您告诉scanf您期望在以'\n'结尾的字符串后面有一个字面上的s。这是一个错误,因为scanf将读取字符串,但不包括'\n'字符*。
  • 当您没有在末尾加上'\n'时,第一个读取将成功,但所有后续读取都会遇到'\n'并立即停止读取。
请注意,对于超过79个字符的输入字符串,能够正常工作的代码可能会产生意外的结果。这些字符串将在第79个字符处被截断,其余部分将作为下一个字符串的开头呈现。
* 当读取到达79个字符的限制时,并且在第80个位置发现字母“s”时,有一种不太可能成功的情况。

@MattMcNabb 哦,我明白你的意思了 - 你是对的,这将解决拆分长行的问题。谢谢! - Sergey Kalinichenko
注意:如果第一个字符是“\n”,则 scanf(“%79 [^ \ n]%* [^ \ n]” 会卡住。它还会消耗多个可能/可能不OK的 '\n' - chux - Reinstate Monica
建议修改:在末尾加上 '\n',这样你就告诉 scanf 在读取字符串后忽略所有连续的行尾字符(以及其他任何空白字符)。 - chux - Reinstate Monica
@chux 它不会消耗多个换行符,而且在末尾使用\n也是不好的,因为它会消耗下一行开头的其他空格。但是你是对的,如果[]匹配了一个空字符串,scanf将返回0 - M.M
更新版本:while( line[0] = 0, scanf("%79[^\n]%*[^\n]",line), getchar() != EOF ) printf("%s\n", line); - M.M
显示剩余2条评论

1

有很多微妙的差别:

1)"%79[^\n]" 告诉 scanf() 扫描并保存 1 到 79 个除了 '\n'char。如果第一个尝试扫描的字符是 \n,则什么也不会被保存,扫描停止。在 OP 的示例中,返回0。否则,将在目标 line 后添加一个 '\0'。如果没有更多内容,则返回1。

2)"%79[^\n]\n" 执行 1),如果成功,则继续查找任何连续的空白字符,不仅限于 '\n'。这通常包括 '\n' 和下一行的所有前导空格。它将返回1,无论是否找到空格。

3) "%79[^\n]s"表示查找一个s,如果成功则返回1。即使找到s,它也会返回1。这可能不是OP所期望的 - 去掉's'

4) "%79[^\n]s\n"执行3),如果成功,则像2)一样继续查找任何连续的空白字符,而不仅仅是'\n'。无论是否找到空格,它都将返回1。

5) 这3个while()语句作为一个整体相当复杂。它在尝试将'\n'读入3个scanf()的开头时被卡住了。

更好的方法是,如果代码需要使用scanf()读取一行,请使用
while (scanf(" %79[^\n]%*c", line) ==1)。这将舍弃前导空格,然后读取一行直到'\n',通过"%*c"抛弃该'\n'

最好的方法是使用fgets(line, sizeof line, stdin)

0

问题可能与逻辑值短路有关。例如,当编译器确定具有足够信息来评估整个表达式时,它不需要测试其他逻辑值(也不会对其进行评估)。

例如,false 和 call_foo() 总是计算为 false,因此 call_foo() 永远不会被调用。

这也可能与 scanf 中的读取行有关(3 个逻辑表达式中的第一个)。其他 scanf 将在下一行读取,而不是同一行,因为第一个 scanf 已经从缓冲区中获取了数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接