Home > Back-end >  sscanf multiple format specifiers to single array
sscanf multiple format specifiers to single array

Time:01-03

Is it possible to run sscanf with multiple format specifiers which will each set a single item in an array? Currently, I have to specify the pointers to each array slot separately, which feels very repetitive:

unsigned int buffer[8];
sscanf(
        line,
        "%6o %6o %6o %6o %6o %6o %6o %6o",
        buffer   0,
        buffer   1,
        buffer   2,
        buffer   3,
        buffer   4,
        buffer   5,
        buffer   6,
        buffer   7);

I'm looking for something similar to JavaScript's spread operator or Python's *-operator for unpacking argument lists:

sscanf(
        line,
        "%6o %6o %6o %6o %6o %6o %6o %6o",
        ...buffer);

At the moment, this fails with the following error:

file.c:42:21: error: format ‘%o’ expects a matching ‘unsigned int *’ argument [-Werror=format=]

Which is kind of expected. How can I tell sscanf to put each format specifier value into consecutive slots in the array? Is it possible? If not, is there a clever workaround (hack)?

CodePudding user response:

How can I tell sscanf to put each format specifier value into consecutive slots in the array? Is it possible? If not, is there a clever workaround (hack)?

Use a loop.

Robust code also detects if line does not contain 8 sets of octal numeric text.


An alternative to strtol() loop is sscanf() with "%6o %n".

" %n" scans optional white-space and then saves the offset of the scan.

size_t n = sizeof buffer / sizeof buffer[0];  // 8

const char *p = line;
size_t i;
for (i = 0; i < n; i  ) {
  int n = 0;
  if (sscanf(p, "%6o %n", &buffer[i], &n) != 1) {  // or use strtol()
    Handle_scanf_failure(); // Add user code here to cope with this error
  }
  p  = n; // Advance p to the next part of the buffer
}
if (*p != '\0') {
  Handle_junk_at_end_of_buffer(); // Add user code here to cope with this error
}

strtol() handles overflow better than "%o", but since we have a max width of 6 octal digits, that would prevent overflow with 32-bit unsigned.

CodePudding user response:

How can I tell sscanf to put each format specifier value into consecutive slots in the array? Is it possible?

According to scanf there is no such conversion specifier.

If not, is there a clever workaround (hack)?

Yes, loop and extract one at a time. strtoul may be useful in this case.

Example:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

size_t scan_uint_array(const char *line, unsigned int buffer[], size_t count, int base)
{
    char *str_end;
    for(size_t i = 0; i < count;   i) {
        unsigned long tmp = strtoul(line, &str_end, base);
        if(str_end == line) return i; // no conversion done
        if(errno == ERANGE) return i; // out of range for ULONG
        if(tmp > UINT_MAX) {          // out of range for UINT
            errno = ERANGE;
            return i;
        }
        buffer[i] = tmp; // all good, save this
        line = str_end;  // move line to where the next scan should be done
    }
    return count;
}

#define Size(x) (sizeof (x) / sizeof *(x))

int main() {
    unsigned int buffer[8];
    size_t ex = scan_uint_array(" 1 2 3 4 5 6 7 ", buffer, Size(buffer), 8);
    
    printf("extracted=%zu wanted=%zu\n", ex, Size(buffer));
    
    for(size_t i = 0; i < ex;   i)
        printf("%u ", buffer[i]);
}

Output

extracted=7 wanted=8
1 2 3 4 5 6 7 
  • Related