2

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"

zogac
  • 89
  • 4
  • "how to code an algorithm" must be first preceded by defining the algorithm. What logic do you wish to use to determine how many strings to read? You could, for example, read one string at a time in a loop and then test if its first character is a digit, then proceed accordingly. – L. Scott Johnson Jun 08 '22 at 13:22
  • For starters, you will never satisfactorily solve this problem using `scanf`. `scanf` is good for some things, but not this. See [What can I use for input conversion instead of scanf?](https://stackoverflow.com/questions/58403537) – Steve Summit Jun 08 '22 at 13:51
  • "I have a struct storing persons' names, surnames and salaries," --> Please post that `struct` definition. – chux - Reinstate Monica Jun 08 '22 at 15:47

2 Answers2

2

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);
}
The Exile
  • 644
  • 8
  • 14
  • @chux-ReinstateMonica Thanks for addressing the problems. Could you check the one I edited? – The Exile Jun 08 '22 at 21:33
  • I fixed the out of range problem. However, I could not understand your next suggestion. Why would not I use dynamic allocation? Does not `sizeof line` evaluated in runtime? Also, I do not know what IAC is. – The Exile Jun 09 '22 at 11:00
  • Sorry for that, I thought IAC is a special thing in computer science. I edited according to your suggestions, could you check? Thanks. – The Exile Jun 09 '22 at 15:46
  • 1
    Much better. Comment cleaner coming though. – chux - Reinstate Monica Jun 09 '22 at 18:38
0

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;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189