Search code examples
carraysstructsegmentation-faultcoredump

Hot to convert TXT File to Array of struct - segmentation fault?


Trying to extract data from a text file and put this data in an array.

File contains a certain amount of lines, each one following the same disposition :

Number FamilyName FirstName GPA

The array needed is an array of struct.

#include<stdio.h>
#include<stdlib.h> 
#include<string.h>
#define MAX 25

typedef struct ppl {
    int login;
    char *p_name;
    char *p_surname;
}person;

int main(void)
{
    char *line = NULL;
    person *data=malloc(MAX*sizeof(person)); 
    float *gpa=malloc(MAX*sizeof(float)); 
    size_t length=0;

    FILE* txtfile;

    if ((txtfile=fopen("file.txt","r"))==NULL) return -1;

    int i=0;               // the counter for the for loop
    int buf_ID;            // buffer variable for the sscanf test
    char buf_name[256];    // same
    char buf_surname[256]; // same
    float buf_grade;       // same

    while ((read = getline(&line, &length,txtfile)) != -1 && i<MAX) 
    {
        if(sscanf(line,"%d %s %s%*s %f", &buf_ID, buf_name, buf_surname, &buf_grade)==4)
        {  
             data[i].login=buf_ID;
             data[i].p_name= malloc(strlen(buf_name)+1);
             strcpy(data[i].p_name, buf_name);
             data[i].p_surname= malloc(strlen(buf_surname)+1);
             strcpy(data[i].p_surname, buf_surname);
             gpa[i]=buf_grade;

             i++;
         }
     }                        

    int j;

    printf("ID | Name | Surname | GPA\n");

    for(j=0;j<i;j++)
    {
        printf("%d | %s | %s | %f\n", data[j].login, data[j].p_name, data[j].p_surname, gpa[j]);
    }
    fclose(txtfile);
    return 0;
}

(Frequently edited code - I'm filling with useless text because the post doesn't get published otherwise)


Solution

  • I see you now allocated memory for your array. Good.

    Now about the char * buffers buf_name etc. Maybe best to do a static allocation here, for exampl

    char buf_name[256];
    

    You are going to re-use this buffer, so static allocation is fine.


    Next step:

    Lookup the getline function: http://man7.org/linux/man-pages/man3/getline.3.html and check that all parameters are correct.


    Now take your debugger and step the program to the sscanf. Step over it and inspect if the variables have been read correctly by sscanf (using a debugger is an important part of learning C).

    The next thing is to ask yourself if your array of persons has all the memory it needs. If not, how are you going to allocate that memory?


    You have allocated an array of persons using malloc. Each person has some data associated with it. The data consist of primitive types and composite types. A primitive type is an int, float, double and char. A composite type consists of a number of these. A string is a composite type because it consists of more than one char.

    So your persons in the array have string data associated with them, as pointers, but the pointers have no memory associated with them. You need to allocate memory to hold the string data. Your data->p_name=malloc(MAX*100);` is not correct.

    With each person you read, you should allocate the string memory to hold the string data that sscanf put in the fixed size buffers:

    data[i].p_name= malloc(strlen(buf_name)+1);
    strcpy(data[i].p_name, buf_name);
    

    and the same for the other string data. (You do this in the loop, of course!)

    Nb: for a primitive type the data is allocated by the compiler; for a composite type you must allocate the memory.

    Nb: a string in C is terminated with a null character. Therefor the memory needed is one more than the length of the string.

    What is that second sscanf doing there in your loop???

    Finally, copy the ID:

    data[i].login= buf_id;
    

    Note that you do not store the grade in the person and gpa has no memory allocated (so you can get a seg fault again).


    Your program will run now without a seg fault. But your loops are incorrect:

    You have a for loop that loops exactly 25 times; within the loop you have a while loop that reads data and puts it in persons. But the index of persons is only updated in the for loop, not in the while loop. Once the last data line has been read, the while loop stops and the next iteration of the for loop starts. However, there is not more data.

    So you need another type of loop, one that loops at most 25 times (as you have no more persons than that) and that stops of there is no more data.

    A for loop can be fine; a while loop can be fine too. But not both. Can you come up with these two variants?


    Your program runs now. As a final point: "clean up!" Remove things no longer needed, make sure the code is properly indented and formatted and add a loop to print the array, so you can check & see it ran correctly.

    I hope you had fun developing this program.