29

Consider the following code:

#include <stdio.h>

int main (void)
{
  char str1[128], str2[128], str3[128];

  printf ("\nEnter str1: ");
  scanf ("%[^\n]", str1);
  printf ("\nstr1 = %s", str1);

  printf ("\nEnter str2: ");
  scanf ("%[^\n]", str2);
  printf ("\nstr2 = %s", str2);

  printf ("\nEnter str3: ");
  scanf ("%[^\n]", str3);
  printf ("\nstr3 = %s", str3);

  printf ("\n");
  return 0;
}

When it is executed only the first scanf stops for the prompt. The program does not stop for the next scanf s. But if the format string is changed from "%[^\n]" to " %[^\n]" (note the blank space before %), then it works okay. Does some existing newline character from the previous input buffer is automatically accepted ? But flushing stdin does not solve this.

What is the cause of this.

Nan Xiao
  • 16,671
  • 18
  • 103
  • 164
phoxis
  • 60,131
  • 14
  • 81
  • 117
  • 4
    Not an answer but your life will be much easier if you look into `fgets` for this one. I generally recommend avoiding `scanf` in most situations, but here especially you're using a very powerful and hard to use function for a very simple task. – Chris Lutz May 21 '11 at 20:50
  • that is okay, there is no problem to input string at my end, but a curiosity about the behaviour or this feature. – phoxis May 22 '11 at 05:17

6 Answers6

44

You just need to 'consume' the '\n' character after you've read what you want. Use the following format directive:

"%[^\n]%*c"

Which will read everything up to the newline into the string you pass in, then will consume a single character (the newline) without assigning it to anything (that '*' is 'assignment suppression').

Otherwise,the newline is left in the input stream waiting to immediately terminate the the subsequent "%[^\n]" format directives.

The problem with adding a space character to the format directive (" %[^\n]") is that the space will match any white space. So, it will eat the newline from the end of the previous input, but it will also eat any other whitespace (including multiple newlines).

Update to your example:

  char* fmt = "%[^\n]%*c";

  printf ("\nEnter str1: ");
  scanf (fmt, str1);
  printf ("\nstr1 = %s", str1);

  printf ("\nEnter str2: ");
  scanf (fmt, str2);
  printf ("\nstr2 = %s", str2);

  printf ("\nEnter str3: ");
  scanf (fmt, str3);
  printf ("\nstr2 = %s", str3);

  printf ("\n");
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • the assignment suppression is a good solution, actually it is the first time i saw it in some good use. – phoxis May 22 '11 at 05:08
  • ... I just spent the past month using "%*1[\n]" for this; +1 and thank you. – David X May 22 '11 at 07:40
  • Is this a de-facto better approach than calling `getchar` to consume the trailing newline? Does this format specification require a specific standard (ie: C99)? Thanks! – Cloud Sep 08 '14 at 16:25
  • 3
    @Dogbert: to be honest, I'm not a big fan of using `scanf()` to parse input. In most cases I'd probably prefer to read a line of input completely into a buffer, then parse that buffer using `sscanf()`, `strtok()` or something even better. – Michael Burr Sep 08 '14 at 20:34
  • @MichaelBurr I figured the same. I just haven't had much, if any, exposure to this `printf` format specifier, save for the `*` and it's always good to see if there's a better habit I should get into. Thanks! – Cloud Sep 08 '14 at 20:57
  • The problem with this version is that it won't match, nor consume if the input is a simple "/n" (the user presses enter without a single charachter) – lerosQ Nov 25 '16 at 21:14
7

When you use scanf() to read the strings, your format string (%[^\n]) tells the function to read every character that is not '\n'. That leaves the '\n' character in the input buffer. So when you try to read str2 and str3, scanf() finds the first thing in the buffer is '\n' each time and, because of the format string, doesn't remove it from the input buffer. What you need is a getchar() between the times that you read from the input buffer (often placed immediately after scanf()). Since there is already a '\n' in the buffer, your program won't appear to hang because it won't have to wait for input for getchar() to receive. Try it. :)

For those who haven't a clue what that scanf() modifier does, here is a relevant excerpt from http://linux.die.net/man/3/scanf -

[

Matches a nonempty sequence of characters from the specified set of accepted characters; the next pointer must be a pointer to char, and there must be enough room for all the characters in the string, plus a terminating null byte. The usual skip of leading white space is suppressed. The string is to be made up of characters in (or not in) a particular set; the set is defined by the characters between the open bracket [ character and a close bracket ] character. The set excludes those characters if the first character after the open bracket is a circumflex (^). To include a close bracket in the set, make it the first character after the open bracket or the circumflex; any other position will end the set. The hyphen character - is also special; when placed between two other characters, it adds all intervening characters to the set. To include a hyphen, make it the last character before the final close bracket. For instance, [^]0-9-] means the set "everything except close bracket, zero through nine, and hyphen". The string ends with the appearance of a character not in the (or, with a circumflex, in) set or when the field width runs out.

  • same question to you also: then what the trailing blank space signifies in " %[^\n]" . This will work correctly. – phoxis May 21 '11 at 17:13
  • 1
    When you add a leading space to the format string, it is evaluated as any character that the function `isspace()` would return a truthy value for, including `'\n'`, `' '`, `'\t'`, etc. This means that the space character before the next non-space character in the format string is ignored. –  May 21 '11 at 17:30
  • If you use a trailing space (a space at the end of the format string), your program appears to hang. Instead of entering a blank line or using space characters, you must enter what the value of str2 will be. The same for str3 and when it asks for str3's value, you would enter a value that won't appear. For example, use `"%[^\n] "` instead of `"%[^\n]"`/`" %[^\n]"`. Your newline will be ignored as specified...and eaten by the trailing space in the format string. Since scanf didn't get a '\n' character, it will want more data. str2 will have that value and str3 will have str2's value. –  May 21 '11 at 17:33
  • 2
    Note that the C99 standard doesn't actually specify the behavior of `"%["`. – Chris Lutz May 21 '11 at 21:13
  • @Chris Lutz: Very true. However, it would seem that it is part of the POSIX.1 specification as both [MSDN](http://msdn.microsoft.com/en-us/library/xdb9w69d(v=vs.71).aspx) and [SuSv2](http://pubs.opengroup.org/onlinepubs/007908799/xsh/fscanf.html) specify it. –  May 21 '11 at 22:25
  • 1
    @Chris - what do you mean C99 doesn't specify `"%["`? It's in 17.9.6.2 "The fscanf function". – Michael Burr May 22 '11 at 03:34
  • i will have a look at what the standards have to say about the scanset. – phoxis May 22 '11 at 05:13
  • @Michael Burr - IIRC my copy (a pre-final draft) left the behavior of `"%["` implementation defined. – Chris Lutz May 22 '11 at 09:34
  • 1
    @Chris: there are parts that are implementation defined, such as using `'-'` to specify a range. – Michael Burr May 22 '11 at 13:44
3

ALSO: To read a string:

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

// it means read until you meet '\n', then trash that '\n'

:)

MLSC
  • 5,872
  • 8
  • 55
  • 89
  • 6
    The final `'\n'` does not only consume a `'\n'` but any number of whitespace `char` like `'\t'` and `' '` as well as the `'\n'`. It will continue consuming (trashing) whitespace until a non-whitespace is found, which is put back into `stdin` for the next IO operation. – chux - Reinstate Monica Jan 06 '14 at 02:47
  • what do you mean by trashing? You mean it is eliminated? – lmiguelvargasf Apr 07 '16 at 20:40
2

Just use a getchar() after the scanf() function.

Abdus
  • 192
  • 3
  • 10
1

Just adding a bit further to above answer- If we want to remove some specific pattern, suppose numbers 0-9, from input stream then we will have to use getchar() for flushing buffer.

scanf("%[^0-9\n]", str1);
while(getchar() != '\n'); // this approach is much better bcz it will
                         // remove any number of left characters in buffer.
scanf("%c", &ch);

So here if you pass ashish019 then only ashish will be copied to str and 019 would be left in buffer so for clearing that you need getchar() multiple times.

Ashish Maurya
  • 131
  • 1
  • 4
-3

use fflush(stdin) to clear the input buffer after reading each input.

  • 3
    `fflush()` is designed to be used only with the output streams. It might seem to work, but the standard defines undefined behavior if used with stdin. **So do not use it with stdin.** – Maxim Chetrusca Nov 16 '14 at 19:04