格式说明符 %[^\n]s 在 C89 中合法吗?

Is the format specifier %[^\n]s legal in C89?

我正在使用 scanf 从 sdin 中读取一个字符串:

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

%[^\n]s 读取直到找到换行符。这在 ANSI-C 中合法吗?

Is the format specifier %[^\n]s legal in C89?

是的,它是“合法的”,因为行为将被定义。

%[^\n] 将扫描除换行符以外的任何内容。 s 将扫描 ss 永远不会匹配,因为缓冲区中会有一个换行符,或者一个错误条件。尽管如此,它还是“有效”的。

出于多种原因,您不应使用此格式:

  • "%[^\n]s"中的s不是转换说明符的一部分,它是一个普通字符,scanf会在读取的字符后尝试匹配该行,在当前字符是换行符或文件末尾的时候。您应该删除 s.

  • scanf("%[^\n]", msg); 将失败并且 return 0,如果用户使用空输入行按 Enter,msg 将保持不变。

  • scanf("%[^\n]", msg); 将导致任何足够长的输入行的缓冲区溢出。要存储到 msg 指向的数组中的最大字符数应在 %[ 之间指定为:

      char buf[100];
      if (scanf("%99[^\n]", msg) == 1) {
          // msg contains the input line, up to 99 characters
          // the remainder if this input line is still in stdin
          // so is the newline
      }
    
  • scanf("%99[^\n]", msg); 不会消耗换行符,如果在换行符之前键入的字符超过 99,则在输入流中留下额外的字符,这可能不是预期的行为。

这是一个更安全的选择:

// read a line of input, discard extra characters and the trailing newline
int mygets(char *dest, size_t size) {
    int c;
    size_t i = 0;
    while ((c = getchar()) != EOF && c != '\n') {
        if (i + 1 < size) {
            dest[i++] = c;
        }
    }
    if (size > 0)
        dest[i] = '[=11=]';
    if (c == EOF && (i == 0 || ferror(stdin)))
        return EOF;
    else
        return (int)i;
}