Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using fscanf() vs. fgets() and sscanf()

Tags:

c

In the book Practical C Programming, I find that the combination of fgets() and sscanf() is used to read input. However, it appears to me that the same objective can be met more easily using just the fscanf() function:

From the book (the idea, not the example):

int main()
{
    int age, weight;
    printf("Enter age and weight: ");

    char line[20];
    fgets(line, sizeof(line), stdin);
    sscanf(line, "%d %d", &age, &weight);

    printf("\nYou entered: %d %d\n", age, weight);
    return 0;
}

How I think it should be:

int main()
{
    int age, weight;
    printf("Enter age and weight: ");
    fscanf(stdin, "%d %d", &age, &weight);

    printf("\nYou entered: %d %d\n", age, weight);
    return 0;
}

Or there is some hidden quirk I'm missing?

like image 937
ankush981 Avatar asked Mar 11 '14 16:03

ankush981


People also ask

What is the difference between fscanf () and fgets ()?

fgets reads to a newline. fscanf only reads up to whitespace.

What is the difference between fscanf and sscanf?

fscanf reads from the named input stream. sscanf reads from the character string s. Each function reads characters, interprets them according to a format, and stores the results in its arguments.

What's the difference between gets () and fgets ()?

fgets() function in C Similar to the gets() function, fgets also terminates reading whenever it encounters a newline character. But furthermore, unlike gets() , the function also stops when EOF is reached or even if the string length exceeds the specified limit, n-1.

Should I use fscanf?

It's almost always a bad idea to use the fscanf() function as it can leave your file pointer in an unknown location on failure. I prefer to use fgets() to get each line in and then sscanf() that. You can always use ftell() to find out current position in file, and then decide what to do from there.


2 Answers

There are a few behavior differences in the two approaches. If you use fgets() + sscanf(), you must enter both values on the same line, whereas fscanf() on stdin (or equivalently, scanf()) will read them off different lines if it doesn't find the second value on the first line you entered.

But, probably the most important differences have to do with handling error cases and the mixing of line oriented input and field oriented input.

If you read a line that you're unable to parse with sscanf() after having read it using fgets() your program can simply discard the line and move on. However, fscanf(), when it fails to convert fields, leaves all the input on the stream. So, if you failed to read the input you wanted, you'd have to go and read all the data you want to ignore yourself.

The other subtle gotcha comes in if you want to mix field oriented (ala scanf()) with line oriented (e.g. fgets()) calls in your code. When scanf() converts an int for example, it will leave behind a \n on the input stream (assuming there was one, like from pressing the enter key), which will cause a subsequent call to fgets() to return immediately with only that character in the input. This is a really common issue for new programmers.

So, while you are right that you can just use fscanf() like that, you may be able to avoid some headaches by using fgets() + sscanf().

like image 64
FatalError Avatar answered Sep 19 '22 21:09

FatalError


There is no difference between fscanf() versus fgets()/sscanf() when:

  1. Input data is well-formed.

Two types of errors occur: I/O and format. fscanf() simultaneously handles these two error types in one function but offers few recovery options. The separate fgets() and sscanf() allow logical separation of I/O issues from format ones and thus better recovery.

  1. Only 1 parsing path with fscanf().

Separating I/O from scanning as with fgets/sscanf allows multiple sscanf() options. Should a given scanning of a buffer not realize the desired results, other sscanf() with different formats are available.

  1. No embedded '\0'.

Rarely does '\0' occurs, but should one occur, sscanf() will not see it as scanning stops with its occurrence, whereas fscanf() continues.

In all cases, check results of all three functions.

like image 45
chux - Reinstate Monica Avatar answered Sep 18 '22 21:09

chux - Reinstate Monica