Using fgets() and strtok() to read in a file line-by-line in C?

41,184

Solution 1

strtok() returns pointers inside line[] so when you read the next line all the pointers you saved are now pointing to places where the last line of the file is stored.

You could allocate memory for each bit of string like so:

//load last name
value = strtok(line, ",");
result[i][0] = malloc(strlen(value) + 1);
strcpy(result[i][0], value);

As an aside, you don't need the loops at the start to set everything to NULL, you could do this instead:

char *result[10][4] = {0};

Solution 2

There are several issues with this code. I've quickly modified your code to do what is expected.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSTRINGSIZE 10
#define LINESIZE 128

struct person{
    char firstName[MAXSTRINGSIZE];
    char lastName[MAXSTRINGSIZE];
    char gender[MAXSTRINGSIZE];
    int birthYear;
    struct person *next;
} *first, *current;


int main (void){
    FILE *myfile = fopen ( "Assignment1file.txt", "r" );
    int i=0;
    int j=0;
    int k=0;
    int l=0;
    char *result[10][4];
    char line[LINESIZE];
    char *value;

    for(i=0; i<=9; i++){
        for(j=0;j<=4;j++){
            result[i][j] = NULL;
        }
    }
    i=0;

    // loop through each entry in Assignment1file
    while(fgets(line, sizeof(line), myfile)){
        //load last name
        value = strtok(line, ", ");
        result[i][0] = strdup(value);
    printf("last: %s\n", value);


        //load first time
        value = strtok(NULL, ", ");
        result[i][1] = strdup(value);
    printf("first: %s\n", value);

        // load gender
        value = strtok(NULL, ", ");
        result[i][2] = strdup(value);
    printf("gender: %s\n", value);

        // load birth year
        value = strtok(NULL, " \n");
        result[i][3] = strdup(value);
    printf("birth year: %s\n", value);

        //go to next line
        i++;
    }   

    // read out the array
    for(k=0; k<5; k++){
        for(j=0;j<4;j++){
            printf("%s\n", result[k][j]);
        }
    }

    fclose(myfile);
    return 0;
}

People have already commented on the details of this change.

Solution 3

strtok will modify the original string. Hence, previous pointers you stored are no longer there after each iteration.

Simple solution is to use: strdup to allocate and copy the values.

Just modify your assignment of value everywhere:

result[i][0] = value;

To:

result[i][2] = strdup(value);

Solution 4

You need to copy the tokens into a separate storage if you need them.

strtok() will modify the buffer that you reads the line in, and replace the delimiters with NUL character, and return a pointer to certain position in the buffer (that is the start of the current token).

When you read in the next line, the buffer will be filled with new data, hence, all the pointers that you have saved are useless, since the previous data is now gone.

Quote from documentation:

To determine the beginning and the end of a token, the function first scans from the starting location for the first character not contained in delimiters (which becomes the beginning of the token). And then scans starting from this beginning of the token for the first character contained in delimiters, which becomes the end of the token.

This end of the token is automatically replaced by a null-character by the function, and the beginning of the token is returned by the function.

And (emphasis mine):

str
C string to truncate. The contents of this string are modified and broken into smaller strings (tokens).
Alternatively, a null pointer may be specified, in which case the function continues scanning where a previous successful call to the function ended.

delimiters:
C string containing the delimiters.
These may vary from one call to another.

Share:
41,184
Heather Wilson
Author by

Heather Wilson

Updated on October 12, 2020

Comments

  • Heather Wilson
    Heather Wilson over 3 years

    I'm trying to use fgets and strtok() to read in a file line by line, and create a linked list of each different line of information.

    Right now, I'm only just putting the information into an array, just to try to figure out how to read the information in correctly, but it's not working right.

    In the while(fgets) portion, it seems to load everything into the array properly, and prints it out. However, after that loop has executed and I try to print out the whole array, I get really weird results.. which is mostly portions of the last line ONLY, and not complete words or anything for the most part.

    For example, if I'm reading in:

    Simpson, Homer, Male, 1976
    Simpson, Marge, Female, 1978
    Simpson, Bart, Male, 2002 
    Simpson, Lisa, Female, 2004 
    Simpson, Maggie, Female, 2011 
    

    The printout I get at the end is something like:

    le
    Simpson
     Maggie
    
    
    Simpson
     Maggie
    e
    ale
    Simpson
     Maggie
    e
    e
    Simpson
     Maggie
     Female
     2011
    

    Please let me know where I'm going wrong, thanks!

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAXSTRINGSIZE 10
    #define LINESIZE 128
    
    struct person{
        char firstName[MAXSTRINGSIZE];
        char lastName[MAXSTRINGSIZE];
        char gender[MAXSTRINGSIZE];
        int birthYear;
        struct person *next;
    } *first, *current;
    
    
    int main (void){
        static const char filename[] = "Assignment1file.txt";
        FILE *myfile = fopen ( "Assignment1file.txt", "r" );
    
        int i=0;
        int j=0;
        int k=0;
        int l=0;
        char *result[10][4];
        char line[LINESIZE];
        char *value;
    
        for(i=0; i<9; i++){
            for(j=0;j<4;j++){
                result[i][j] = NULL;
            }
        }
        i=0;
    
        // loop through each entry in Assignment1file
        while(fgets(line, sizeof(line), myfile)){
    
            //load last name
            value = strtok(line, ",");
            result[i][0] = value;
            printf("%i 0 %s", i, value);
    
    
            //load first time
            value = strtok(NULL, ",");
            result[i][1] = value;
            printf("%i 1 %s", i, value);
    
            // load gender
            value = strtok(NULL, ",");
            result[i][2] = value;
            printf("%i 2 %s", i, value);
    
            // load birth year
            value = strtok(NULL, "\n");
            result[i][3] = value;
            printf("%i 3 %s", i, value);
            printf("\n");
    
            for(j=0;j<4;j++){
                printf("%s\n", result[i][j]);
            }
    
    
            //go to next line
            i++;
        }   
    
        // read out the array
        for(k=0; k<5; k++){
            for(j=0;j<4;j++){
                printf("%s\n", result[k][j]);
            }
        }
    
        fclose(myfile);
        return 0;
    }
    
  • nhahtdh
    nhahtdh almost 12 years
    strdup is not standard C function. Can be implemented, though: stackoverflow.com/questions/252782/strdup-what-does-it-do-in‌​-c
  • S.S. Anne
    S.S. Anne about 5 years
    This wouldn't necessarily be useful if the other answers were removed. Do you mind adding some details?