3

I'm reading a string from sdin with a scanf by doing:

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

%[^\n]s reads till a new line character is found. Is this legal in ANSI-C?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Foxen
  • 35
  • 6

2 Answers2

6

You should not use this format for multiple reasons:

  • The s in "%[^\n]s" is not part of the conversion specifier, it is a plain character that scanf will try to match after the characters read from the line, at a time where the current character is either a newline or the end of file. You should remove the s.

  • scanf("%[^\n]", msg); will fail and return 0, leaving msg unchanged if the user hits enter with an empty input line.

  • scanf("%[^\n]", msg); will cause a buffer overflow for any sufficiently long input line. The maximum number of characters to store into the array pointed to by msg should be specified between the % and the [ as:

      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); does not consume the newline and leaves extra characters in the input stream if more than 99 were typed before the newline, which may of may not be the expected behavior.

Here is a safer alternative:

// 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] = '\0';
    if (c == EOF && (i == 0 || ferror(stdin)))
        return EOF;
    else
        return (int)i;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    Very good. To make better: `if (c == EOF && (i == 0 || !feof(stdin)) return EOF;` to return `EOF` on a rare input error that just occurred. Could return a wider signed type to handle very _long_ lines. – chux - Reinstate Monica Dec 26 '21 at 23:47
  • 1
    @chux-ReinstateMonica: you are correct, but I am reluctant to show event correct uses of `feof()` so I shall use `ferror()`. Regarding the return type, I guess I could use `int` for the buffer size, as `fgets()` does for historical reasons :) – chqrlie Dec 27 '21 at 00:03
5

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

Yes, it's "legal", as in the behavior will be defined.

The %[^\n] will scan anything except a newline character. s will scan an s. s will never match, because there will be a newline in the buffer, or an error condition. Still, it's "valid".

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 2
    *the behavior will be defined*... except if the user inputs more characters than will fit in the destination array, in which case the behavior is undefined. – chqrlie Dec 26 '21 at 23:14