4

Is it possible for fscanf to consume input and return zero at the same time? For example, if I write

int res;
int k = fscanf(f, "%d", &res);

and check that k == 0, can I be sure that the next invocation of fscanf on the same file f would continue at the same spot where the file was prior to the invocation of fscanf?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • You should consider also using `%n` in your `fscanf` *after* the `%d` – Basile Starynkevitch Dec 15 '17 at 20:43
  • @BasileStarynkevitch If I put `%n` after `%d` and provide a string that does not represent an integer, the `fscanf` call would return zero, and %n would remain unmodified. [Here is a quick demo](https://ideone.com/B1EpR8). – Sergey Kalinichenko Dec 21 '17 at 19:23

3 Answers3

3

This is true only for one of the three conversion specifiers that do not consume leading whitespace — %c, %[…] (scan sets), and %n. All other conversions, including %d, will consume whitespace even if the data that they are trying to read is not in the correct format.

Here is an example that demonstrates this behavior:

int main(void) {
    int ignore; char c;
    int a = scanf("%d", &ignore);
    int b = scanf("%c", &c);
    printf("%d %d %c\n", a, b, c);
    return 0;
}

If you pass non-numeric input to this program with leading whitespace, scanf("%c", &c) will read the first non-whitespace character (demo).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
3

Another variant on the theme outlined in dasblinkenlight's answer is:

for (int i = 0; i < 20; i++)
{
    int rc;
    int number;
    if ((rc = scanf(" this is the time for all good men (all %d of them)", &number)) == 0)
    {
        char remnant[4096];
        if (fgets(remnant, sizeof(remnant), stdin) == 0)
            printf("Puzzling — can't happen, but did!\n");
        else
        {
            printf("The input did not match what was expected.\n");
            printf("Stopped reading at: [%s]\n", remnant);
        }
    }
    else if (rc == 1)
        printf("%d: There are %d men!\n", i + 1, number);
    else
    {
        printf("Got EOF\n");
        break;
    }
}

Try it on a file containing:

this is the time for all good men (all 3 of them)
this is the time for all good men (all 33 men)
   this   is   the
      time      for

all     good      men
(all

333 of

     them)
       this is the time for all good men to come to the aid of the party!

Etc.

Output:

1: There are 3 men!
2: There are 33 men!
The input did not match what was expected.
Stopped reading at: [men)
]
4: There are 333 men!
The input did not match what was expected.
Stopped reading at: [to come to the aid of the party!
]
Got EOF

Note that the conversion succeeded on the second sentence, even though the matching failed on 'men)' (where 'of them)' was expected). There is no reliable way to get information about matching failures after the last counted (non-suppressed, non-%n) conversion. The next attempt to match failed altogether, but the fgets() cleaned up the input (read the residue of the line), and then the subsequent attempt succeeded, because the blanks in the format string match arbitrary sequences of white space in the input. In the last line of the example data, the information 'this is the time for all good men' was read successfully, but 'to come to the aid of the party' did not match.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

Is it possible for fscanf to consume input and return zero at the same time?

Consider input "x"

scanf("%*d"); 
printf("%d\n", getchar());

The expected output would be , the ASCII code for 'x' which was pushed back and then re-read with getchar().

Now consider input "-x". With my platform the result is 45, the ASCII code for '-'.

An input item is read from the stream, .... An input item is defined as the longest sequence of input characters .... which is, or is a prefix of, a matching input sequence. 285) The first character, if any, after the input item remains unread. (C11 §7.21.6.2 ¶9)

285) fscanf pushes back at most one input character onto the input stream. Therefore, some sequences that are acceptable to strtod, strtol, etc., are unacceptable to fscanf.

As to my understanding of the spec, this should result in a 120 ('x'), as the prefix portion "-" is read and consumed even if it is more sensible to put 2 characters back. See also @Jonathan Leffler for a floating-point case.

So it is possible to consume non white space input and return zero at the same time.


The consumption of white space is well answered here as well as non white space as part of a longer format.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256