2

I know for a fact that [^\n] is a delimiter that makes scanf to scan everything until "enter" key is hit. But I don't know what the remainder of "%[^\n]%*c" is used for. Also why do we have to mention "&s" instead of "s" in the scanf function.

I tried running this:

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

Both the above scanf statements worked exactly the same for me. If there is any difference between them, What is it?

Also Why should I prefer scanf("%[^\n]%*c", &s); to the above declarations?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Alagusankar
  • 111
  • 1
  • 8
  • For an array that has decayed to a pointer, as in your scanf call, `s`, `&s`, and `&s[0]` all refer to the same address. Where did you hear you should prefer `scanf("%[^\n]%*c", &s);`? – Retired Ninja Apr 14 '19 at 05:07
  • 2
    Quite similar, Possible duplicate of [What does \`scanf("%\*\[^\n\]%\*c")\` mean?](https://stackoverflow.com/questions/30065675/what-does-scanf-nc-mean) – Achal Apr 14 '19 at 05:07
  • 5
    `scanf("%[^\n]%*c", &s);` is akin to `gets()`. Use neither. Instead use `fgets(s, sizeof s, stdin)`. – chux - Reinstate Monica Apr 14 '19 at 05:09
  • 1
    You should prefer `fgets()`, but if you must `%[^\n]` is a `[...]` (character class) format specifier matching all characters that are `'^'` ('not' when included 1st) a newline. (which allows reading whitespace). The `'*'` character is the *assignment suppression* modifier that allows reading/discarding the type for that *conversion specifier* (a single char here, e.g. the `'\n'`). When `'*'` is used, any conversion is not added to the *match count* (e.g. the return). – David C. Rankin Apr 14 '19 at 05:12
  • This declaration was originally used in Hackerrank. Why to go for fgets instead of scanf("%[^\n]s",s) ? – Alagusankar Apr 14 '19 at 05:14
  • 1
    Because what remains in your input buffer after the call does not depend on the conversion specifier used (or any reading/discarding of additional characters). Just use `fgets` and trim the newline by overwriting the the *nul-terminating* character -- it will save many headaches. – David C. Rankin Apr 14 '19 at 05:15

2 Answers2

5

scanf("%[^\n]s",&s); has many troubles:

No check of return value.
No overrun protection
No consumption of '\n'
No assignment of s on '\n' only
No need for s in "%[^\n]s"
Wrong type &s with format.

scanf("%[^\n]s",s); only has one less problem (last one).

Use fgets(s, sizeof s, stdin)

if (fgets(s, sizeof s, stdin)) {
  s[strcspn(s, "\n")] = 0; // Lop off potential ending \n
  // Success
} else {
  // failure
}

  • No check of return value.

Without checking the return value, success of reading is unknown and the state of s indeterminate.

  • No overrun protection

What happens when the 100th character is inputted? - "Very bad. Not good. Not good." Necron 99, Wizards 1977

  • No consumption of '\n'

'\n' remains in stdin to foul up the next read.

  • No assignment of s on '\n' only

If the input begin with '\n', scanf() returns and s unchanged. It is not assigned "".

  • No need for "s" in "%[^\n]s"

The "s" is not part of the specifier "%[^\n]". Drop it.

  • Wrong type &s with format.

%[...] matches a char *, not a pointer to a char[100] like &s (UB).

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Sound advice. Of course SO would lose 30% of questions were it not for the pitfalls surrounding the misuse of `scanf` `:)` – David C. Rankin Apr 14 '19 at 05:17
  • I am a newbie here. Thanks for the swift and elaborate answer but it would help me much more if you could explain more on your listed bullet points as I still can't get the grasp of it. – Alagusankar Apr 14 '19 at 05:18
  • @DavidC.Rankin Today `scanf()`, [tomorrow the world!!!!](http://littlefun.org/posts/Today_you_tomorrow_the_world) – chux - Reinstate Monica Apr 14 '19 at 05:19
  • I want your picture collection `:)` – David C. Rankin Apr 14 '19 at 05:20
  • @Alagusankar - take bullet 1 -- how to fix? (check the return). Bullet 2 - use the *field-width modifier* see [scanf(3) - Linux manual page](http://man7.org/linux/man-pages/man3/scanf.3.html), Bullet 3 - you request a match of a literal `'s'` following a collection of characters not a `'\n'`, Bullet 4 follows from 3, Bullet 5 - explains `%[...]` is a complete *conversion specifier* in its own right, the `'s'` that follows must match a literal `'s'` in the input (your use is superfluous)., Bullet 6 follows from 5. (or make life easy, use `fgets()`) – David C. Rankin Apr 14 '19 at 05:24
1

You can take a string as input in C using scanf(“%s”, s). But, it accepts string only until it finds the first space.

In order to take a line as input, you can use scanf("%[^\n]%*c", s); where s is defined as char s[MAX_LEN] where MAX_LEN is the maximum size of s . Here, [] is the scanset character. ^\n stands for taking input until a newline isn't encountered. Then, with this %*c, it reads the newline character and here, the used * indicates that this newline character is discarded.

Note: After inputting the character and the string, inputting the sentence by the above mentioned statement won't work. This is because, at the end of each line, a new line character (\n) is present. So, the statement: scanf("%[^\n]%*c", s); will not work because the last statement will read a newline character from the previous line. This can be handled in a variety of ways and one of them being: scanf("\n"); before the last statement.

Alagusankar
  • 111
  • 1
  • 8