NewToAndroid
NewToAndroid

Reputation: 581

Sscanf not behaving as intended in extracting strings

I have a SPARQL query like "select ?Y ?Z where ...". All that I want to do is extract the variables "?Y" and "?Z". I wrote the following loop:

char *p = "select ?Y ?Z where condition";
char *array[2];

p += strlen("select");        /* advancing the pointer */

for(int i = 0; i < 2; i++)
{
        sscanf(p, "%m[?a-zA-Z] %[^\n]", array[i], p);    /* 'm' tells sscanf to perform dynamic memory allocation */
        printf("%s, ", array[i]);
        printf("%s, ", p);
}

However, I am not getting the intended behavior. array[1] contains garbage and array[2] contains null.

Upvotes: 0

Views: 291

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 753695

You have several problems. Two are correctly diagnosed by zubergu in his answer: (1) passing a char * where a char ** is needed (see scanf()), and (2) trying to overwrite the string you are scanning which is readonly in the first place and in any case invokes undefined behaviour.

Another is that you do not check the return status from sscanf(). You probably need to something like this:

char *p = "select ?Y ?Z where condition";
char *array[2];

p += strlen("select");

for (int i = 0; i < 2; i++)
{
    int offset;
    if (sscanf(p, " %m[?a-zA-Z]%n", &array[i], &offset) != 1)
        ...report error and break loop...
    printf("%s, ", array[i]);
    p += offset;
}

Notice the blank before the conversion specification to skip leading spaces.

If you have a version of sscanf() that supports the notation, this code should work correctly:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char *p = "select ?Y ?Z where condition";
    char *array[2] = { 0, 0 };
    int i;
    int j;

    p += strlen("select");

    for (i = 0; i < 2; i++)
    {
        int offset;
        printf("p = <<%s>>\n", p);
        if (sscanf(p, " %m[?a-zA-Z]%n", &array[i], &offset) != 1)
            break;
        printf("%d: <<%s>> (offset = %d)\n", i, array[i], offset);
        p += offset;
    }
    printf("%d: all done\n", i);

    for (j = 0; j < i; j++)
        free(array[j]);
    return 0;
}

Mac OS X 10.9 does not support the m modifier; neither do older versions of Linux (note to self: must update available VMs). When tested on an Ubuntu 12.04 derivative, I got the output:

p = << ?Y ?Z where condition>>
0: <<?Y>> (offset = 3)
p = << ?Z where condition>>
1: <<?Z>> (offset = 3)
2: all done

Upvotes: 1

zubergu
zubergu

Reputation: 3706

Based on http://man7.org/linux/man-pages/man3/scanf.3.html
where in example there is

char *p;
n = scanf("%m[a-z]", &p);

I assume that you should change your

sscanf(p, "%m[?a-zA-Z] %[^\n]", array[i], p);

to

sscanf(p, "%m[?a-zA-Z] %[^\n]", &array[i], p);

Look how %m is paired with char ** not with char*.

I suggest reading THIS discussion, too.

I also noticed that your p is char * and you initialize it with string constant, then try to overwrite it with call to sscanf.

Besides that, you read from and write to memory p points to in the same function call. I'm not 100% sure here, but that's probably undefined behaviour right there.

Hope it helps.

Upvotes: 2

Related Questions