【问题标题】:Is there a syntax like this in C? %[^\n]sC中有这样的语法吗? %[^\n]s
【发布时间】:2016-09-04 03:52:18
【问题描述】:

班上的一位同学告诉我,使用 scanf 扫描字符串时会使用该语法,但没有限制。我的意思是,你可以使用 scanf 扫描一个字符串,它仍然会扫描空格。

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

【问题讨论】:

  • 是不是更像scanf("%[^\n]",string);
  • 如果第一个字符是 '\n'scanf("%[^\n]s",&string); 将保持 string 不变。它也容易受到溢出攻击。请改用fgets()

标签: c string syntax scanf


【解决方案1】:

scanf( "%[^\n]", string ); 将读取所有直到(但不包括)换行符的所有内容到string1。与s 转换说明符不同,它不会跳过任何前导空格(空格、制表符等)。

正如所写,这段代码容易受到缓冲区溢出的影响2;如果string 的大小可以容纳10 个字符,而有人输入了100 个字符,那么这些多余的字符将紧跟string 写入内存,这可能会导致各种混乱。

至少,您会想要添加一个字段宽度说明符,例如

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

表示“将不超过 10 个字符读入string”。不幸的是,必须将字段宽度指定为格式字符串的一部分;与printf 不同,scanf 不提供将字段宽度指定为参数的方法。

您可以动态构建格式字符串,例如:

char fmt[20];
char string[11] = {0};

sprintf( fmt, "%%%zu[^\n]", sizeof string - 1 );
scanf( fmt, string ); // fmt contains the string "%10[^\n]"

这样您就不必对字段宽度进行硬编码。

或者,您可以完全避免胃灼热,而只使用fgets

char string[11];
...
fgets( string, sizeof string, stdin ); 

语义上的主要区别在于fgets 将换行符存储到string(如果有空间),因此如果您不希望字符串中出现换行符,您将拥有手动删除:

char *newline = strchr( string, '\n' );
if ( newline )
  *newline = 0;   // overwrite newline character with 0 terminator


  1. 这意味着您将在输入流中出现一个杂散的换行符,这可能会也可能不会导致下一个输入出现问题。
  2. 它在功能上或多或少等同于 gets 库函数,正是出于这个原因,它已从 C 2011 中删除(有关“或更少”部分,请参见下面 chux 的评论)。

【讨论】:

  • scanf( "%[^\n]" 在功能上与gets() 不同。 1)当输入只有"\n"时,gets(string)形成string --> ""scanf( "%[^\n]",string)string没有任何作用。 2)gets() 消耗最后的'\n'scanf( "%[^\n]" 不消耗。正如你所说,fgets() 是更好的方法。
猜你喜欢
  • 2021-04-02
  • 2013-07-26
  • 2013-12-31
  • 2015-12-09
  • 2022-07-11
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 1970-01-01
相关资源
最近更新 更多