为什么在scanf格式字符串中添加前导空格是推荐的?

8

我目前正在阅读的这本书是《C11编程入门》,在scanf()章节中提到:

“始终在第一个控制字符串字符之前添加一个前导空格,以确保准确的字符输入。” 例如:

scanf(" %s", whatever);

控制字符串" %s"前面有一个空格。 我知道这听起来很简单明了,但我不太明白可能会出现什么问题,以及这个空格如何确保准确的字符输入。

4
最好加上一个重要的警告。单个字符 %c 获取很可能是该过度强调语句背后的主要原因,未提供具体细节使我质疑该文本的巫术教学思想。一个没有任何解释的 "总是做<blah>" 的说法,不说明为什么你应该遵循这个口号,以及如果你这样做会出现什么问题,往往比有帮助的更令人困惑(就像你发这个问题一样...)。无论如何, 您可以在这个答案中找到帮助。 - WhozCraig
那本书真的叫做《C11编程入门》吗?在C11中,scanf不是被scanf_s所取代了吗? - Anders Marzi Tornblad
@AndersMarziTornblad: 不是的。 scanf_s() 是一个可选函数,定义在附录K §3.5.3.4《scanf_s 函数》中(http://port70.net/~nsz/c/c11/n1570.html#K.3.5.3.4)。微软更希望您使用他们自己的版本 scanf_s(),但很少有其他编译器/库实现它。这些函数的签名是一致的,这与 _s(更安全)函数不同。 - Jonathan Leffler
"%s" 的意思是:忽略可选的前导空格并读取以空格分隔的字符串... " %s" 的意思是忽略可选的空格,然后忽略可选的前导空格并读取以空格分隔的字符串。 - pmg
2个回答

6

@WhozCraig非常明确地指出了这条建议的缺点。它没有合理性,也没有细节。此外,它不适用于"%s",因为"%s"会在有或没有前导空格的情况下消耗前导空格。

前导空格,无论是' ''\t''\n'等,都有同样的作用:直接让scanf()消耗而不存储可选的前导空格char。这在以前的scanf()的典型使用中非常有用,因为它不会消耗用户的Enter

scanf("%d", &some_int);
scanf("%c", &some_char);  // some_char is bound to get the value '\n'

所有scanf()输入说明符都忽略前导空格,除了三个:"%c""%n""%[]"
在以下指令中,前导空格确实有用。在'$'之前会消耗前面剩余的空白。
int Money;
scanf(" $%d", &Money);

通常情况下,在读取用户输入时,"%c" 前加上一个空格是有益的,但并非总是如此。
char ch;
scanf(" %c", &ch);

这个建议最大的问题在于:1)使用"%s"时,提供一个宽度参数是确保代码稳健性必要的,2)应该检查返回值。
char buf[30];
int cnt = scanf("%29s", buf);
if (cnt != 1) Handle_NoInput_or_EOF_IOError();

注意,除了%c%[…](扫描集)和%n之外的所有转换说明符都会自动跳过前导空格,因此该建议实际上与%s%d%lf等无关。
最后,我建议在可以使用时尽可能使用fgets()而不是scanf()

谢谢 chux :) 就我所理解的,当使用 scanf() 和 "%c"、"%n" 或 "%[]" 一起使用时,scanf() 会在字符中存储一个前导空格。如果一个 char 只能容纳一个字符,它会如何在同一 char 中存储? - Stanley
3
scanf(" %c", &ch); 会忽略前导空格并将非空格字符存储在 ch 中。而 scanf("%c", &ch); 则将第一个出现的字符存储在 ch 中。"%n""%[]" 的工作方式截然不同。 - chux - Reinstate Monica
非常好的答案,感谢您为这个网站所做的努力! - VimNing

1

“在第一个控制字符串字符之前始终添加一个前导空格,以确保准确的字符输入。”

这是为了消耗可能由先前用户输入(如回车符)留下的stdin中的任何尾随空格字符或前导空格字符,然后scanf读取用户输入。当然,除了%c%[…](扫描集)和%n之外的所有转换都会自动执行此操作,因此这个建议对于%s并不重要。


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