cscanf

In s/fscanf(), how to ensure that the entire format string was matched against entire input?


The *scanf() family of functions return the number of "input items" (that is, conversion specifications) successfully matched and assigned:

RETURN VALUE

On success, these functions return the number of input items successfully matched and assigned; this can be fewer than provided for, or even zero, in the event of an early matching failure.

Now, consider a format string that contains some conversion specifications interleaved with some literal characters and ends in one or more literal characters:

// read the response to a CSI 14 t request
int r = sscanf(input, "\033[4;%d;%dt", &resp_y, &resp_x);

Consider four potential inputs to this sscanf call:

  1. "\033[4;100;200t"
  2. "\033[4;100;200"
  3. "\033[4;100;200foobar"
  4. "\033[4;100;200tfoobar"

As far as I can understand, in this case the return value (r) will be set to 2 in all three cases. However, only input (1) is valid, while inputs (2) and (3) are invalid, and input (4) contains trailing data.


What is the cleanest / most correct way to distinguish (1) from (2) and (3) and from (4), i.e. ensure that the entire format string was successfully matched against the entire input?

I'm interested in answers to this question for both sscanf() and fscanf().


Solution

  • Use:

    int n = 0;
    int r = sscanf(input, "\033[4;%d;%dt%n", &resp_y, &resp_x, &n);
    if (r != 2 || n == 0)
    {
        …scanning failed…
    }
    

    Note that the assignment to n is not counted as a 'conversion' so it isn't counted in the return value.

    If you want to ensure that the whole string was used with sscanf(), then you can check:

    if (r == 2 && n != 0 && input[n] == '\0')
    {
       …whole string was scanned…
    }
    

    This technique using %n can be used with any of the scanf() family of functions. Although I've linked to POSIX documentation, the C standard says the same things. If you're reading from a file (fscanf() or scanf() rather than sscanf()), you probably don't want to check the value of n more than shown in the first snippet. If you want to test for a newline (or other white space), you can't do that so easily with scanf() — see What is the effect of trailing white space in a scanf() format string?.