Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't scanf("%*[^\n]\n"); and scanf("%*[^\n]%*c"); clearing a hanging newline?

Tags:

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
"
like image 301
IanC Avatar asked Jan 30 '17 02:01

IanC


1 Answers

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.

like image 116
Chris Dodd Avatar answered Sep 22 '22 10:09

Chris Dodd