Using scanf()
or its relatives is hard; very hard.
All the conversion specifiers except %c
, %[…]
(scan sets) and %n
skip leading white space. That makes it hard to apply a numeric conversion for a fixed width.
All the code below tacitly assumes that there are no more than 20 numbers on the line — it doesn't check bounds to ensure that (but operational code most certainly should — or could use dynamic memory allocation to allocate more space for numbers when needed).
Bogus code — looks simple, but fails
/* SO 4564-1845 */
#include <stdio.h>
#include <string.h>
int main(void)
{
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
int nbr[20];
int num = 0;
int n;
int val;
int off = 0;
while (sscanf(line + off, "%3d%n", &val, &n) == 1)
{
printf("Got: %d - [%s] %d\n", val, line + off, n);
off += n;
nbr[num++] = val;
}
for (int i = 0; i < num; i++)
printf("%3d", nbr[i]);
putchar('\n');
}
return 0;
}
Example output
This program was called rd31
. Note there is debug output here too:
$ rd31 < data
Got: 128 - [128 6 -3224
] 3
Got: 6 - [ 6 -3224
] 3
Got: -32 - [ -3224
] 4
Got: 24 - [24
] 2
128 6-32 24
$
The %3d
format skips the leading blank in " -3"
and takes the 2
of 224
as the third character. This isn't what you want.
Working code — more complex, but passes
This code keeps using sscanf()
, invoking it twice per number, which may be sub-optimal.
/* SO 4564-1845 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
line[strcspn(line, "\n")] = '\0';
int nbr[20];
int num = 0;
int n;
int val;
char data[4];
int off = 0;
while (sscanf(line + off, "%3c%n", data, &n) == 1)
{
data[n] = '\0';
if (sscanf(data, "%d", &val) != 1)
{
fprintf(stderr, "Oops! [%s]\n", data);
break;
}
printf("Got: [%s] - [%s] = %d (%d)\n", data, line + off, val, n);
off += n;
nbr[num++] = val;
}
for (int i = 0; i < num; i++)
printf("%3d", nbr[i]);
putchar('\n');
}
return 0;
}
This has different diagnostic output. It reads the data into a string, and then converts the string. Using data[n] = '\0';
nulls the end of what was read.
Example output
This program was called rd53
.
$ rd53 < data
Got: [128] - [128 6 -3224] = 128 (3)
Got: [ 6] - [ 6 -3224] = 6 (3)
Got: [ -3] - [ -3224] = -3 (3)
Got: [224] - [224] = 224 (3)
128 6 -3224
$
This is what you want, I believe.
Alternative code — using sscanf()
just once per value
This code uses memmove()
to copy data from the line into a buffer for conversion.
/* SO 4564-1845 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
line[strcspn(line, "\n")] = '\0';
int nbr[20];
int num = 0;
int val;
char data[4];
int off = 0;
int len = strlen(line);
while (off < len)
{
memmove(data, line + off, 3);
data[3] = '\0';
if (sscanf(data, "%d", &val) != 1)
{
fprintf(stderr, "Oops! [%s]\n", data);
break;
}
printf("Got: [%s] - [%s] = %d\n", data, line + off, val);
off += 3;
nbr[num++] = val;
}
for (int i = 0; i < num; i++)
printf("%3d", nbr[i]);
putchar('\n');
}
return 0;
}
You could also use atoi()
or strtol()
to convert the string to a number. Overflow isn't an issue with at most 3 digits to convert. Still, non-numeric data would cause some minor issues; you can protect against them better if you use strtol()
, but doing so requires considerable care. (See Correct usage of strtol()
? for more details.)
Example output
This program was called rd89
.
$ rd89 < data
Got: [128] - [128 6 -3224] = 128
Got: [ 6] - [ 6 -3224] = 6
Got: [ -3] - [ -3224] = -3
Got: [224] - [224] = 224
128 6 -3224
$