Home > Software design >  How to take unknown number of string from file in C?
How to take unknown number of string from file in C?

Time:06-10

I have a struct storing persons' names, surnames and salaries, but the number of their names is random. For example:

list.txt

John Smith Green 1000 //He has two names
Jennifer Wilson 2000 //She has one name
Steve Adams 1500 //He has one name
James Robert Harris 1250 //He has two names
Robin Walker 1750 //He has one name

I want to store their names in person[count].name, their surnames in person[count].surname and their salaries in person[count].salary.

To do that, I wrote:

fscanf(file, "%s %s %d", person[count].name, person[count].surname, &person[count].salary)

However, problem is that if a person has two names, his second name is stored in person[count].surname, and I cannot take the surname.

How can I take the name of a person with two names in person[count].name?

For this text file:

person[0].name ==> "John Smith"

person[1].name ==> "Jennifer"

person[2].name ==> "Steve"

person[3].name ==> "James Robert"

person[4].name ==> "Robin"

CodePudding user response:

I tried reading the file line by line, then separating it into tokens (words). I am assuming each line contains at max 10 tokens (words), and the last token is salary, the one before the last is surname and first N-2 tokens are the names of the person. So, each person could have surnames with only one word, I am assuming. Here is the code, note that I did not pay attention to memory leaks or dangling pointers etc.

I edited the solution according to the suggestions from @ chux - Reinstate Monica

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

typedef struct Person
{
    char *name, *surname;
    int salary;
} Person;

int main()
{
    Person persons[10];
    FILE *file = fopen("text.txt", "r");
    if (file == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }
    // read file line by line
    char line[256];
    int person_count = 0;
    while (fgets(line, sizeof(line), file) != NULL)
    {
        char *tokens[10];
        int i = 0;

        tokens[0] = strtok(line, " ");
        while (tokens[i] != NULL && i < 9)
        {
            tokens[  i] = strtok(NULL, " ");
        }

        char name[sizeof line];
        strcpy(name, tokens[0]);
        for (int j = 1; j < i - 2; j  )
        {
            strcat(name, " ");
            strcat(name, tokens[j]);
        }
        persons[person_count].name = strdup(name);
        persons[person_count].surname = strdup(tokens[i - 2]);
        persons[person_count].salary = atoi(tokens[i - 1]);
        person_count  ;
    }
    for (int i = 0; i < person_count; i  )
    {
        printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
    }

    fclose(file);
}

CodePudding user response:

You cannot use fscanf() for this problem. Here is a simple approach reading one line at a time and parsing it explicitly with strrchr():

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

struct Person {
    char *name, *surname;
    int salary;
};

int main() {
    const char *filename = "list.txt";
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        fprintf(stderr, "Cannot open %s: %s\n", filename, strerror(errno));
        return 1;
    }
    // read file line by line
    char line[256];
    struct Person *persons = NULL;
    int count = 0;
    while (fgets(line, sizeof line, file) != NULL) {
        char *last = strrchr(line, ' ');
        if (!last)  // invalid format
            continue;
        *last   = '\0';
        int salary;
        if (sscanf(last, "%d", &salary) != 1)
            continue;
        const char *name = line;
        char *surname = strrchr(line, ' ');
        if (surname) {
            *surname   = '\0';
        } else {
            name = "";  // handle Superman: no first name
            surname = line;
        }
        persons = realloc(persons, (count   1) * sizeof(*persons));
        if (persons == NULL) {
            fprintf(stderr, "out of memory\n");
            return 1;
        }
        persons[count].name = strdup(name);
        persons[count].surname = strdup(lastname);
        persons[count].salary = salary;
        count  ;
    }
    fclose(file);

    // dump the database
    for (int i = 0; i < count; i  ) {
        printf("%s %s %d\n", persons[i].name, persons[i].surname, persons[i].salary);
    }

    // free the database
    for (int i = 0; i < count; i  ) {
        free(persons[i].name);
        free(persons[i].surname);
    }
    free(persons);
    return 0;
}
  • Related