Not understanding the C format specifiers when using fscanf()

213 views Asked by At

So I am reading a text file in this format:

ABC 51.555 31.555
DEF 23.445 45.345

I am trying to use fscanf() to parse the data, because this file could grow or shrink it needs to be dynamic in the way it loads hence why i used malloc and i also want to store it in the struct below. I think the issue is with a space or even possible not writing the whole format specifier right. Here is my code.

typedef struct data
{
    char name[4];
    char lat[7];
    char lng[7];
}coords;

int main(int argc, char *argv[])
{
    ////////////CREATES FILE POINTER/////////
    FILE* fp;
    ///////////CREATES MALLOC POINTER TO STORE STRUCTS/////////////
    coords* cp;
    //////////OPENS FILE//////////
    fp = fopen(argv[1], "r");
    /////////GET THE TOTAL AMMOUNT OF LINES IN THE FILE/////////
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    rewind(fp);
    //////SKIPS FIRST LINE//////////
    while(fgetc(fp) != (int)'\n')
    {};

    /////////ASSIGNS MEMORY THE SIZE OF THE FILE TO //////////
    cp = malloc(sizeof(coords) * size);

    //////////READS FILE AND STORES DATA///////
    fscanf(fp,"%s[^ ] %s[^ ] %s[^\n]", cp->name, cp->lat, cp->lng);

    printf("%s\n%lf\n%lf\n", cp->name, cp->lat, cp->lng);
    fclose(fp);
    return 0;
}

And yes I am aware I did not include the header files but I have got the right ones stdlib and stdio

UPDATE 1: I have tried both replies and I get this on my screen:

ABC51.555
0.000000
0.000000

How come the 51.555 has not gone to the next item in the struct? Thanks

///////////////////////////////////////////////////////////////UPDATE 2////////////////////////////////////////////////////////

Okay I have modified my code to do the following.

typedef struct data
{
    char name[4];
    char lat[6];
    char lng[6];
}coords;

int main(int argc, char *argv[])
{
    ////////////CREATES FILE POINTER/////////
    FILE* fp;
    ///////////CREATES MALLOC POINTER TO STORE STRUCTS/////////////
    coords* cp;
    //////////OPENS FILE//////////
    fp = fopen(argv[1], "r");
    /////////GET THE TOTAL SIZE OF THE FILE/////////
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    long lines = -1;
    rewind(fp);
    //////GETS TOTAL AMMOUNT OF LINES/////////
    char c;
    while(c != EOF)
    {
        c = fgetc(fp);
         if(c == '\n')
         {
            lines++;
         }
    }
    rewind(fp);
    ////////////SKIPS FIRST LINE//////////
    while(fgetc(fp) != (int)'\n')
    {};
    /////////ASSIGNS MEMORY THE SIZE OF THE FILE TO //////////
    cp = malloc(sizeof(coords) * size);

    //////////READS FILE AND STORES DATA///////
    printf("Lines of text read: %d\n", lines);
    fscanf(fp,"%s %s %s[^\n]", cp[0].name, cp[0].lat, cp[0].lng);
    printf("%s\n", cp[0].name);


    fclose(fp);
    return 0;
}

Now when i try to print cp[0].name; I get the whole of the first line with no space in, like this.

 ABC51.55531.555

If i got print cp[0].lat; I get this.

 51.55531.555

And when i print cp[0].lng; I get this.

 31.555

Which is the only correct one, I can not understand this behaviour. Why is it behaving like this? all the posts suggest (As i first thought) that each %s in fscanf would put it in to its own variable not concatenate them. Not mater if i use the dot notation or the direct -> it still has the same result. Thanks :)

2

There are 2 answers

1
Chris Dodd On

The format specifier "%s[^... attempts to read a whitspace delimited string, followed by the character [ and then the character ^. Since the string will always end at whitespace, the next character will always be whitespace, which won't match the [, and none of the rest of the format specifier will match.

  • ALWAYS check the return value of fscanf to make sure you read all the things you thing you did. If the return value is wrong, give a diagnostic.

  • ALWAYS use field size limits when reading into fixed size string arrays.

So in your case what you want is:

if (fscanf(fp, "%3s%6s%6s", cp->name, cp->lat, cp->lng) != 3) {
    fprintf(stderr, "Incorrect data in input file, exiting!\n");
    abort(); }
0
buydadip On

I'm not sure that you want to use the space delimiter [^ ]. fscanf already parses the string on whitespace as default. Try this and see if the string is correctly parsed:

fscanf(fp, "%s %s %s[^\n]", cp->name, cp->lat, cp-lng);  

output should result in:

cp->name ---- ABC
cp->lat ----- 51.555
cp->lng ----- 31.555