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;
}