Having read a line as you are doing, you need to split it into item entries. Since you're using strtok()
, I assume that you don't need to identify the delimiter. Also, one of the reasons for disliking strtok()
and preferring Microsoft's strtok_s()
on Windows and POSIX strtok_r()
everywhere else is that you can't have nested calls to strtok()
, but you need them. Or you need to use scanf()
, or some other technique.
Warning: none of this code has been near a compiler!
char *next_ptr = NULL;
char *next_item = strtok_r(line, ",;", &next_ptr);
while (next_item != NULL){
char *item_ptr = NULL;
char *name = strtok_r(next_item, "-", &item_ptr);
if (name == NULL)
{
fprintf(stderr, "Failed to scan name out of [%s]\n", next_item);
break;
}
int price;
next_item = strtok_r(NULL, " ,", &item_ptr);
assert(next_item != NULL);
if (sscanf(next_item, "%d", &price) != 1)
fprintf(stderr, "Failed to convert [%s] to integer\n", next_item);
else
printf("Item: [%s] price %d\n", name, price);
next_item = strtok_r(NULL, ",;", &next_ptr);
}
The break
on error is because I'm being lazy. Ideally, you should continue to the next item in the line, but that involves rewriting the loop as a for
loop:
for (next_item = strtok_r(line, ",;", &next_ptr);
next_item != NULL;
next_item = strtok_r(NULL, ",;", &next_ptr))
{
...body of loop except for final call to strtok_r()...
}
Now you can use continue
and the next_item = strtok_r(NULL, ",;", &next_ptr)
statement will be executed, and that's crucial. You might need to worry about an empty item after the semicolon; add && *next_item != '\0'
to the loop condition.
If you can't use strtok_r()
or strtok_s()
for some reason (what?), then you can consider using:
char name[30];
int price;
if (sscanf(next_item, "%29[^-] - %d", name, &price) != 2)
{
fprintf(stderr, "Failed to extract item name and price from [%s]\n", next_item);
continue; // Or break!
}
And there are other options too — many of them, in fact.
Note that strtok()
and its relatives all (a) destroy the input string and (b) do not tell you which character marked the end, and (c) treats multiple separators as one, so you can't spot empty items between two delimiters (which isn't a problem in this example). The destruction is chopping the original string into shorter substrings by inserting null bytes where the delimiter was found.
Note that if an item name contains a dash, you're hosed with this system. You'll interpret the 'Vacuum-24' as being an item named 'Vacuum' with 24 as the price. You can work around that, too, but it is harder (think strstr()
to find " - "
(blank, dash, blank) as a separator between item name and price, for example).