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

Solution 1:

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().

Solution 2:

The problem with only using fscanf() is, mostly, in error management.

Imagine you input "51 years, 85 Kg" to both programs.

The first program fails in the sscanf() and you still have the line to report errors to the user, to try a different parsing alternative, to something;

The second program fails at years, age is usable, weight is unusable.

Remeber to always check the return value of *scanf() for error checking.

    fgets(line, sizeof(line), stdin);
    if (sscanf(line, "%d%d", &age, &weight) != 2) /* error with input */;

Edit

With your first program, after the error, the input buffer is clear; with the second program the input buffer starts with YEAR...

Recovery in the first case is easy; recovery in the second case has to go through some sort of clearing the input buffer.

Solution 3:

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.