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