1

Can someone explain how does this work? I know that it reads a string(with spaces), but I don't really understand the mechanism behind. Can someone explain it to me piece by piece?

scanf("%[^\n]%*c",string);
ad absurdum
  • 15,925
  • 4
  • 28
  • 49
  • Have you tried reading the man page? if you're on a unix system type `man scanf` and read through it. – EdgeCaseBerg Nov 15 '16 at 18:29
  • In particular, the final `%*c` instructs to read the terminating `newline` as a `char` and discard the result, so as not to leave the `newline` in the input buffer. – Weather Vane Nov 15 '16 at 18:32
  • Here is a good reference of regex: http://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean – RandomEli Nov 15 '16 at 18:32
  • Do you mean you want to know what each element the format string (the `"%[^\n]%*c"`) does, or how the function itself is implemented? – cynic Nov 15 '16 at 18:32
  • 1
    @LingboTang-- this is not a regular expression. – ad absurdum Nov 15 '16 at 18:37
  • You can simply treat `%` as a mark of place holder, `[^\n]` means capture a group of characters except the `newline`, then`%*c` means consume the line by chars. – RandomEli Nov 15 '16 at 18:38
  • These are conversion specifiers, and `%*c` means that one `char` should be read, and its assignment is suppressed. – ad absurdum Nov 15 '16 at 18:42

2 Answers2

3
scanf("%[^\n]%*c",string);

This says "read everything until a newline character and then read one character". * (in %*c) is to suppresses the assignment. That's it reads a line and consumes the newline character.

From scanf():

An optional '*' assignment-suppression character: scanf() reads input as directed by the conversion specification, but discards the input. No corresponding pointer argument is required, and this specification is not included in the count of successful assignments returned by scanf().

But I'd, for reading lines, use fgets() instead and consume the trailing newline afterwards.

char string[256];
if (fgets(string, sizeof string, stdin) == NULL) {
    /* handle failure */
}
string[strcspn(string, "\n")] = 0; /* For removing the newline if present */

which is less error prone and better to understand.

If you are using glibc, you can use getline() as well.

P.P
  • 106,931
  • 18
  • 154
  • 210
  • Note that the `scanf()` format fails to read empty lines and causes buffer overflow on lines longer that the destination array. The `fgets()` approach is much preferable, but you should test the return value before attempting to strip the `'\n'` as the contents of `string` are indeterminate if `fgets()` fails and returns `NULL`. – chqrlie Nov 15 '16 at 20:01
2

From the man page:

All conversions are introduced by the % (percent sign) character

A conversion is how we match certain text strings. For example a %s matches a string, and %d matches a decimal integer. So looking at your string, we have a "%[ conversion, which according to the man page:

[ Matches a nonempty sequence of characters from the specified set of accepted characters; ... the set is defined by the characters between the open bracket [ character and a close bracket ] character.

So this conversion is going to define a list of characters which will be matched and read into your string. Important to this is:

The set excludes those characters if the first character after the open bracket is a circumflex ^.

And if you look at your string "%[^\n]%*c" you've got %[^\n] which means that you're matching any character until you hit a newline character.

Next, you have a %* the star is a conversion which ignores what matches after it. From the man page:

Suppresses assignment. The conversion that follows occurs as usual, but no pointer is used; the result of the conversion is simply discarded.

So if you look at your last match, you've got a c,

c Matches a sequence of width count characters (default 1);

so the %*c means that you're going to match 1 character, and then discard it (the character being matched is the newline - which the %[^\n] didn't consume because you matched everything up to that newline), it won't be stored in your string variable.

Reading the man page is your friend. I hope this helps.

EdgeCaseBerg
  • 2,411
  • 1
  • 18
  • 37