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"

3
  • "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. Commented Jun 8, 2022 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? Commented Jun 8, 2022 at 13:51
  • "I have a struct storing persons' names, surnames and salaries," --> Please post that struct definition. Commented Jun 8, 2022 at 15:47

2 Answers 2

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

@chux-ReinstateMonica Thanks for addressing the problems. Could you check the one I edited?
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.
Sorry for that, I thought IAC is a special thing in computer science. I edited according to your suggestions, could you check? Thanks.
Much better. Comment cleaner coming though.
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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.