Search code examples
cparsingformatscanfformat-string

fscanf doesn't recognise delimeters defined in format string (


I'm trying to read user login data (username, password, some integer, security answer) from a file and store it in a linked list. The file validation.txt holds the data for all users, where each user has a line of its own holding all of the fields for that user in the format (no spaces):

user1_$_ password1 _$_42 _$_answer1

user2_$_ password2 _$_21 _$_answer2

The various fields are to be stored as members of a struct (named 'user') that comprises the linked list (each struct also has a member "next" which points to the next user in the list.

I trying to use fscanf to parse each line and save the info directly into the structs, but for some reason, the fscanf doesn't pick up on $ when it reaches them, and stores an entire line under the "username" member rather than just the string "user1". I have read loads online about the string format but can't figure this out. I played around trying to add whitespaces to the file and to the format string with many different outcomes that I didn't really understand why they are the way they are.. Based on what I read online the string should be read until a character present in the format string is reached, and it is then skipped over. This seems pretty straightforeward but why doesn't this work ?

My code:

user *loadUsers(){

    user *Users, *currPtr;
    FILE *fp = fopen("validation.txt", "r");
    if(fp==NULL||feof(fp)!=0){
        return NULL;
    }
    
    Users = (user *)malloc(sizeof(user));
    currPtr = Users;
    fscanf(fp, "%s_$_%s_$_%d_$_%s\n", currPtr->username, currPtr->password, &(currPtr->randomNum), currPtr->secAns);
    while(feof(fp)==0){
        currPtr->next = (user *)malloc(sizeof(user));
        currPtr = currPtr->next;
        fscanf(fp, "%s_$_%s_$_%d_$_%s\n", currPtr->username, currPtr->password, &(currPtr->randomNum), currPtr->secAns);
    }
    return Users;
}

Output:

When I iterate through my list after the call to this function and print just the username members of each struct, I get the whole line printed (username = user1_$_ password1 _$_42 _$_answer1 rather than just user1). Anyone know what's going on here ?


Solution

  • Never, ever, call *scanf() without checking the return value.

    %s matches any sequence of non-whitespace characters, i.e. fscanf() keeps matching that first %s and does not even "see" the following underscore in the format string.

    You could work with %[^_] to match everything not an underscore, but generally speaking you are better off reading whole lines of input with fgets() and then parse the input in memory using the various string functions. *scanf() is rather limited in its ability to recover from malformed input.