There is absolutely no need to use strtok()
at all, because strtol()
sets the pointer pointed by the second parameter to point to the character following the number parsed.
A complete example program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
long *number = NULL;
size_t numbers = 0;
size_t numbers_max = 0;
char *curr, *next, *ends;
long temp;
size_t i;
while (1) {
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len < 1)
break;
curr = line_ptr;
ends = line_ptr + line_len;
numbers = 0;
while (1) {
/* Parse next long. */
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break;
if (next == curr)
break;
/* Need to grow number array first? */
if (numbers >= numbers_max) {
size_t temp_max = (numbers | 1023) + 1025 - 16;
long *temp_ptr;
temp_ptr = realloc(number, temp_max * sizeof number[0]);
if (!temp_ptr) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
numbers_max = temp_max;
number = temp_ptr;
}
/* Save parsed number. */
number[numbers++] = temp;
/* Skip trailing whitespace, */
curr = next;
while (curr < ends && (*curr == '\t' || *curr == '\n' || *curr == '\v' ||
*curr == '\f' || *curr == '\r' || *curr == ' '))
curr++;
/* Skip separator. */
if (*curr == '|')
curr++;
else
break; /* No separator, so that was the final number. */
}
printf("Parsed %zu longs:", numbers);
for (i = 0; i < numbers; i++)
printf(" %ld", number[i]);
printf("\n");
fflush(stdout);
}
if (ferror(in)) {
fprintf(stderr, "Error reading standard input.\n");
exit(EXIT_FAILURE);
}
free(line_ptr);
line_ptr = NULL;
line_max = 0;
free(number);
number = NULL;
numbers = 0;
numbers_max = 0;
return EXIT_SUCCESS;
}
Other than the available memory, this program has no limits wrt. line length or the amount of numbers it stores in the array. The growth policy for the number array is funky (just my style); feel free to replace it with anything you prefer. Just make sure temp_max
is at least numbers + 1
. Making it larger means you allocate more at once, and therefore do fewer "slow" realloc() calls.
The outer while
loop iterates over lines read from standard input.
The inner while
loop parses the longs from that line, separated by a pipe character |
. strtol()
ignores leading whitespace. In case there is whitespace between the number and the following pipe character, we need to explicitly skip that; you could also use just while (curr < ends && isspace(*curr)) curr++;
for that.
If you want to collect all the longs into a single array, rather than per line, just omit the numbers = 0;
before the inner while
loop. (And move printing out the numbers after the outer while
loop.)
The actual conversion,
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break; /* errno == ERANGE; number too large in magnitude! */
if (next == curr)
break; /* end of input, no number */
relies on the fact that if the number to be converted is too large in magnitude, strtol()
will set errno = ERANGE
and return LONG_MIN
(if the number in the string was negative) or LONG_MAX
(if positive). To detect that, we must set errno
to zero first. If the string is empty (or there is a stray nul char, \0
, in the line), strtol()
will return 0 with next == curr
.