5

After a call to scanf("%d", &variable); we are left with a newline hanging at the stdin, which should be cleared before a call to fgets, or we end up feeding it a newline and making it return prematurely.

I've found answers suggesting using scanf("%*[^\n]%*c"); after the first call to scanf to discard the newline and others suggesting using scanf("%*[^\n]\n");. Theoretically, both should work: The first would consume everything that isn't a newline (but not including the newline itself) and then consume exactly one character (the newline). The second would consume everything that isn't a newline (not including it) and then \n, a whitespace character, would instruct scanf to read every whitespace characters up to the first non-whitespace character.

However, as much as I've seem those approaches working in some answers, I couldn't get them to work here (codes below).

Why neither of the scanf approaches worked?

Tested on: Ubuntu Linux - gcc 5.4.0

scanf("%*[^\n]\n"); approach:

#include <stdio.h>

int main(int argc, char **argv){
    int number;
    char buffer[1024];

    printf("Write number: \n");
    scanf("%d", &number);

    //Clearing stdin?
    scanf("%*[^\n]\n");

    printf("Write phrase: \n");
    fgets(buffer, 1024, stdin);

    printf("\n\nYou wrote:%u and \"%s\"\n", number, buffer);

    return 0;
}

Output:

$ ./bug 
Write number: 
2
Write phrase: 


You wrote:2 and "
"

scanf("%*[^\n]%*c"); approach:

#include <stdio.h>

int main(int argc, char **argv){
    int number;
    char buffer[1024];

    printf("Write number: \n");
    scanf("%d", &number);

    //Clearing stdin?
    scanf("%*[^\n]%*c");

    printf("Write phrase: \n");
    fgets(buffer, 1024, stdin);

    printf("\n\nYou wrote:%u and \"%s\"\n", number, buffer);

    return 0;
}

Output:

$ ./bug2 
Write number: 
3
Write phrase: 


You wrote:3 and "
"

The following approach was the only one that worked the way it was expected to:

Working approach:

#include <stdio.h>

int main(int argc, char **argv){
    int number;
    char buffer[1024];

    printf("Write number: \n");
    scanf("%d", &number);

    //Clearing stdin!
    int c;
    while((c = getchar()) != '\n' && c != EOF){
        //Discard up to (and including) newline
    }

    printf("Write phrase: \n");
    fgets(buffer, 1024, stdin);

    printf("\n\nYou wrote:%u and \"%s\"\n", number, buffer);

    return 0;
}

Output:

$ ./notbug 
Write number: 
4
Write phrase: 
phrase :D


You wrote:4 and "phrase :D
"
IanC
  • 1,968
  • 14
  • 23
  • you may use fgets(or gets) to read the input into buffer, then use sscanf() to get the number. – Shiping Jan 30 '17 at 02:11
  • @Shiping Sure, there are other approaches to get this code working. But the point of the question is why the `scanf` approaches of clearing the hanging newline aren't working as it would be expected. – IanC Jan 30 '17 at 02:21
  • 1
    @Shiping: **never ever** use `gets`. It is dangerous and not part of the standard (anymore). – too honest for this site Jan 30 '17 at 02:39
  • 1
    @IanC: So do you notice any correlation why experienced C programmers tell to use `fgets` instead of using `scanf`? – too honest for this site Jan 30 '17 at 02:41
  • @Olaf Yeah! `fgets` is more straight-up to the point and doesn't create that issue of hanging characters on the *stdin* buffer. I actually ran into this doubt while writing an answer to a question that used `scanf` – IanC Jan 30 '17 at 02:52
  • @Olaf thanks. I hardly write interactive C programs now. even if i do need some simple input from users, i usually ask users to give as commandline arguments. – Shiping Jan 30 '17 at 02:53
  • Voting to close because `scanf` is behaving as it's specified according to the standard. – autistic Jan 30 '17 at 06:16

1 Answers1

4

The basic problem is that the scanf pattern %[^\n] matches ONE OR MORE characters that are not newlines. So if the next character is a newline, the pattern will fail and scanf will return immediately without reading anything. Add a * doesn't change that basic fact. So it turns out you can't do this with only one call, you need two:

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

Note that putting a bare newline into the scan pattern is almost always not useful -- it causes scanf to read and discard all whitespace until it sees a non-whitespace character (which will be left in the input buffer). Particularly if you try to use it in an interactive program, it will appear to hang until you enter a non-blank line.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • Thank you very much! It makes sense now why `scanf` didn't seem to discard anything at all from the *stdin*, it was actually failing right at the beginning. I could confirm what you were saying on the first line of `[` conversion specifier description ("Matches a **nonempty** sequence of characters from the specified set...") and on the "Return Value" section :) – IanC Jan 30 '17 at 02:32
  • Corner case: `scanf("%*[^\n]");` returns because it encountered a `'\n'`, end-of-file or input error. `scanf("%*c");` is OK to call after the first 2 of those 3. In the last, rare case, `scanf("%*c");` may consume a non- `'\n'` character. Checking the return value of `scanf()` is recommended. – chux - Reinstate Monica Jan 30 '17 at 16:52
  • @chux: If the first `scanf` gets an input error, then so will the second, as there's no `clearerr` call between them. Same with an EOF. – Chris Dodd Jan 30 '17 at 17:41
  • C explicitly says "If the end-of-file indicator for the stream is set, _or_ if the stream is at end-of-file, the end-of-file indicator for the stream is set and the fgetc function returns EOF." There is _not_ a corresponding _or_ with the error indicator: "If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF". So the 2nd `scanf()` may return a non-EOF, even though the first returned EOF due to an input error - with/without an intervening `clearerr()`. – chux - Reinstate Monica Jan 30 '17 at 18:06