views:

1136

answers:

5

I have a comma separated string which might contain empty fields. For example:

1,2,,4

Using a basic

sscanf(string,"%[^,],%[^,],%[^,],%[^,],%[^,]", &val1, &val2, &val3, &val4);

I get all the values prior to the empty field, and unexpected results from the empty field onwards.

When I remove the expression for the empty field from the sscanf(),

sscanf(string,"%[^,],%[^,],,%[^,],%[^,]", &val1, &val2, &val3, &val4);

everything works out fine.

Since I don't know when I'm going to get an empty field, is there a way to rewrite the expression to handle empty fields elegantly?

A: 

If you use strtok with the comma as your separator character you'll get a list of strings one or more of which will be null/zero length.

Have a look at my answer here for more information.

ChrisF
Yes, that is the route I'll go if I can't find a way to adapt the sscanf. I'm still hoping I can just adapt my sscanf, but as far as I can tell [^,] doesn't match empty sequences.
Belrog
@Belrog, it seems that `sscanf`'s internal counter get stuck if the string contains `,,`. So better use `strtok`.
Nick D
I was really hoping to not have to do that. I have a long list of values of different types (chars/ints/floats). That would mean a field counter and a large switch statement to assign it correctly. *sigh*
Belrog
A: 

man sscanf:

[ Matches a nonempty sequence of characters from the specified set of accepted characters;

(emphasis added).

Sinan Ünür
A: 

This looks like you are currently dealing with CSV values. If you need to extend it to handle quoted strings (so that fields can contain commas, for example), you will find that the scanf-family can't handle all the complexities of the format. Thus, you will need to use code specifically designed to handle (your variant of) CSV-format.

You will find a discussion of a set CSV library implementations in 'The Practice of Programming' - in C and C++. No doubt there are many others available.

Jonathan Leffler
A: 

scanf() returns the number of items assigned. Maybe you can use that info ...

char *data = "1, 2,,, 5, 6";
int a[6];
int assigned = sscanf(data, "%d,%d,%d,%d,%d,%d", a, a+1, a+2, a+3, a+4, a+5);
if (assigned < 6) {
    char fmt[18];
    switch (assigned) {
        default: assert(0 && "this did not happen"); break;
        case 0: fmt = ",%d,%d,%d,%d,%d"; break;
        case 1: fmt = "%d,,%d,%d,%d,%d"; break;
        case 2: fmt = "%d,%d,,%d,%d,%d"; break;
        case 3: fmt = "%d,%d,%d,,%d,%d"; break;
        case 4: fmt = "%d,%d,%d,%d,,%d"; break;
        case 5: fmt = "%d,%d,%d,%d,%d,"; break;
    }
    sscanf(data, fmt, a+(assigned<=0), a+1+(assigned<=1), a+2+(assigned<=2),
                      a+3+(assigned<=3), a+4+(assigned<=4));
}

Ugh! And that's only for 1 missing value
As has been pointed out by other answers, you're much better off parsing the string in the 'usual' way: fgets() and strtok().

pmg
A: 
sambowry