Saving data from a file to a struct in c - c

I have two structs that I have to fill with student data. The data is in the format:
age, name, grade, age, group, turn
and in the header of the file is the number of student in the data.
struct school //school
{
char group; //A,B,C,D,E,F
char turn; //Morning, AFTERNOON
};
struct student
{
char *name;
char *grade;
int age;
struct school *E;
}student[6];
I tried to save first the data from a text with only the age, name, and grade to see if I could do it:
void get_file(const char* file, int *n){ //n is the amount of students
FILE* fptr;
fptr = fopen(file, "r");
if (fptr == NULL){
printf( "\n Error \n");
exit(1);
}
char* temp;
int tam = 0;
fscanf(fptr, "%d", n); //size of the list of students
for(int i= 0; i < *n; i++){
fscanf(fptr, "%d,%s,%s", &student.age[i],temp, student[i].grade);
tam = strlen(temp);
student[i].name = (char*)malloc(tam * sizeof(char));
strcpy(student[i].name, temp);
printf("%s\n", student[i].name);//to see if it's correct the content
}
fclose(fptr);
}
But, student.name store for example "Josh, A+" when it should only be "Josh". How can I fix this?
It's for an assignment.
EDIT:
My data looks like this
4 //size of list
Josh,A,20,D,M
Amber,B,23,E,M
Kevin,C,22,D,A
Adam,A+,21,C,A
Using the Remy Lebeau's solution, I got this
void get_file(const char* file, int *n){
*n = 0;
FILE* fptr = fopen(file, "r");
if (fptr == NULL){
printf( "\n Error \n");
exit(1);
}
char name[80];
char grade[2];
fscanf(fptr, "%d", n); //size of the list of students
for(int i = 0; i < *n; i++){
fscanf(fptr, "%80[^,],%2[^,],%d,%c,%c", &student[i].age, name, grade,&student[i].group, &student[i].turn);
student[i].name = strdup(name);
student[i].grade = strdup(grade);
}
fclose(fptr);
}
But I got a problem, because I made this change
struct student
{
char *name;
char *grade;
int age;
struct school E; //it was struct school *E
}student[6];
to pass the information, but my Teacher said that I couldn't change it, so how can I load information in struct school *E?

There are multiple problems with your call to fscanf():
&student.age[i] needs to be &student[i].age.
temp and student::grade are uninitialized pointers, they don't point anywhere, so reading data to memory pointed by them is undefined behavior. fscanf() will not allocate memory for you, you need to pre-allocate char[] buffers yourself for fscanf() to then read into.
%s reads non-whitespace characters including ',', which is why your code is not stopping on the , following Josh. Try using %[^,] instead of %s for that field.
Try something more like this:
void get_file(const char* file, int *n){
*n = 0;
FILE* fptr = fopen(file, "r");
if (fptr == NULL){
printf( "\n Error \n");
exit(1);
}
char name[256];
char grade[5];
fscanf(fptr, "%d", n); //size of the list of students
for(int i = 0; i < *n; i++){
fscanf(fptr, "%d,%255[^,],%4s", &student[i].age, name, grade);
student[i].name = strdup(name);
student[i].grade = strdup(grade);
printf("%d %s %s\n", student[i].age, student[i].name, student[i].grade);
}
fclose(fptr);
}
UPDATE:
The file data you have now shown does not match the format you described, which your code is expecting. You described (and coded) the file data as:
age, name, grade, age, group, turn (why 2 ages?)
But what you have shown looks more like:
name, grade, age, group, turn
You changed your format string in fscanf() to this new format, but you didn't change the order of the variables being read into to match this format. So, you are reading the name value into your age field, the grade value into the name field, and the age value into the grade field.
Also, you are not even attempting to handle group and turn correctly. They reside in a completely different structure, which you are not allocating any memory for. You need to allocate a school object, read group and turn into it, and then assign it to student[i].E.
With that said, try something more like this:
struct school
{
char group; //A,B,C,D,E,F
char turn; //Morning, AFTERNOON
} school[6];
struct student
{
char *name;
char *grade;
int age;
struct school *E;
}student[6];
void get_file(const char* file, int *n){
*n = 0;
FILE* fptr = fopen(file, "r");
if (fptr == NULL){
printf( "\n Error \n");
exit(1);
}
char name[81];
char grade[3];
fscanf(fptr, "%d", n); //size of the list of students
for(int i = 0; i < *n; i++){
student[i].E = &school[i]; // or, use malloc() instead, if needed...
fscanf(fptr, " %80[^,],%2[^,],%d,%c,%c", name, grade, &(student[i].age), &(student[i].E->group), &(student[i].E->turn));
student[i].name = strdup(name);
student[i].grade = strdup(grade);
}
fclose(fptr);
}

Related

Reading content from a .txt file and storing in a struct using fscanf

So I'm having trouble making a program that reads content from a .txt file and store it in a struct. The file looks more or less like this in the inside:
number that tells how many employees have their information stored in the file
name (string)
salary (float like 2000.00)
the date of when they started at the job (in the format dd/mm/yyyy like 22/10/2020)
department (string)
...
I've tried many ways of doing this (like writing fscanf(input, "%d/%d/%d",...) and things just as weird) and looked for help in lots of places, but it just doesn't work, I can't find what's wrong. Any help would be much appreciated!
Here's the part of the code I'm talking about:
typedef struct dt
{
int day;
int month;
int year;
} date;
typedef struct if
{
char name[50];
float salary;
date start;
char department[50];
} info;
int main(int argc, char **argv){
int i, n;
FILE *input;
input = fopen(argv[1], "r");
if(input == NULL)
{
printf("Failed to open the file");
return 1;
}
fscanf(input, "%d", &n);
info database[n];
fseek(input, sizeof(n), SEEK_SET);
for(i = 0; i < n; i++)
{
fgets(database[i].name, 100, input);
fscanf(input, "%f", &database[i].salary);
fscanf(input, "%2d", &database[i].start.day);
fscanf(input, "%2d", &database[i].start.month);
fscanf(input, "%4d", &database[i].start.year);
fgets(database[i].department, 25, input);
}
I trying to fix your code, read the fixed code below and my comment on the line that's cause the problem. Also i prefer fscanf instead of fgets
#include <stdio.h>
typedef struct dt{
int day;
int month;
int year;
} date;
typedef struct inf{
char name[50];
float salary;
date start;
char department[50];
} info;
int main(int argc, char **argv){
int i, n;
FILE *input;
input = fopen(argv[1], "r");
if(input == NULL)
{
printf("Failed to open the file");
return 1;
}
fscanf(input, "%d", &n);
info database[n];
// fix sizeof(n) to sizeof(n)-1
fseek(input, sizeof(n)-1, SEEK_SET);
for(i = 0; i < n; i++)
{
// using fscanf to get name
fscanf(input, "%100s",database[i].name);
fscanf(input, "%f", &database[i].salary);
// using fscanf to get day/month/year
fscanf(input, "%2d/%2d/%4d", &database[i].start.day, &database[i].start.month, &database[i].start.year);
// using fscanf to get the department string
fscanf(input, "%25s", database[i].department);
}
}
here is my sample text file
3
john
200.00
22/10/2020
lolba
jea
250.00
22/10/2120
lilba
jug
270.00
22/10/2220
logba

Im having a problem with reading a file, then assigning it to a struct array and printing it back out

Im currently having to take an input.txt file, where it goes something like
3
Sarah 90 40 30
John 23 55 33
help 34 99 74
as an input file,
and read it into a struct array, then create an output.txt.
I seem to be having a problem with the assignment. I tried fscanf, fgetc, fgets, strtok, delim and everything i could find on the internet, but due to my sloppy pointer knowledge, i seem to be stuck.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct studentT{
char *name;
int literature;
int math;
int science;
}studentT;
int main(void)
{
printf("Start of main\n");
FILE *fptr;
int i;
fptr = fopen("input.txt", "r");
//reading first line to dynamically allocate studentT
printf("dynamically allocating studentT\n");
char tempsize[4];
//fgets(tempsize,1,fptr);
//i=atoi(&tempsize[0]);
fscanf(fptr,"%d",&i);
struct studentT* record = malloc(i*sizeof(*record));
printf("i has interger value %d\n", i);
//line counter ignoring line 0; reading 1 and 2.
char line[24];
char buffer[24];
char delim[] = " ";
char *array[4];
int *darray[4];
printf("entering while loop\n");
fgets(buffer,24,fptr);
int idx =0;
while(fgets(line, sizeof(line),fptr)!=NULL &&idx<i)
{
puts(line);
char *buf = strtok(line, delim);
int iter =0;
while(buf!=NULL)
{
if(iter = 0){
array[iter]=buf;
buf = strtok(NULL,delim);
}
for(iter=1;iter<4;iter++){
*darray[iter] =atoi(buf);//
printf("%d,",*darray[iter]);
buf=strtok(NULL, delim);
}
}
record[idx].name=array[0];
record[idx].literature=*darray[1];
record[idx].math=*darray[2];
record[idx].science=*darray[3];
// printf("array value: %s %d %d %d\n",array[0], array[1], array[2], array[3]);
// printf("value after casting: %s %zu %zu %zu\n", array[0], (uintptr_t)array[1], (uintptr_t)array[2], (uintptr_t)array[3]);
//sscanf(buf,"%s %d %d %d",record[idx].name, &record[idx].literature, &record[idx].math, &record[idx].science);
printf("%s\n", line);
//fscanf(fptr,"%s %d %d %d",record[idx].name, &record[idx].literature, &record[idx].math, &record[idx].science);
line[strlen(line)-1]='\0';
printf("in idx loop %d %s %d %d %d\n\n", idx, record[idx].name, record[idx].literature, record[idx].math, record[idx].science);
idx++;
}
//output of file
printf("output commencing\n\n\n");
int tempave;
FILE *fout;
char *str1 = "Name Literature Math Science Ave.\n";
char *str2 = "Ave. ";
char *str3 = "Sarah\t 96\t 90\t 80\t 88.67";
char *str4 = "Minsu\t 55\t 70\t 76\t 67.00";
char *str5 = "Nara\t 88\t 70\t 96\t 84.67";
char *str6 = "79.67 76.67 84.00 80.11";
fout = fopen("output.txt", "w");
fprintf(fout,"%s\n", str1);
for(int idx=0;idx<i;idx++){
tempave = (record[idx].math+record[idx].literature+record[idx].science)/3;
fwrite(&record[idx],sizeof(struct studentT),1,fout);
fprintf(fout, "%d\n",tempave);
}
//fprintf(fout, "%s\n %s\n %s\n %s %s\n",str3,str4,str5,str2,str6);
fprintf(fout,"%s",str2);
float mathave,litave,sciave;
for(int idx=0;idx<3;idx++){
mathave+=record[idx].math;
litave+=record[idx].literature;
sciave+=record[idx].science;
}
mathave=mathave/3;
sciave=sciave/3;
litave=litave/3;
fprintf(fout,"%f %f %f",litave, mathave, sciave);
fclose(fptr);
fclose(fout);
}
edit: colleague mentioned I should malloc arrays so the code now looks uglier:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct studentT{
char *name;
int literature;
int math;
int science;
}studentT;
int main(void)
{
printf("Start of main\n");
FILE *fptr;
int i;
fptr = fopen("input.txt", "r");
//reading first line to dynamically allocate studentT
printf("dynamically allocating studentT\n");
char tempsize[4];
//fgets(tempsize,1,fptr);
//i=atoi(&tempsize[0]);
fscanf(fptr,"%d",&i);
struct studentT* record = malloc(i*sizeof(*record));
printf("i has interger value %d\n", i);
//line counter ignoring line 0; reading 1 and 2.
char line[24];
char buffer[24];
char delim[] = " ";
char *array[4];
int darray[4];
printf("entering while loop\n");
fgets(buffer,24,fptr);
int idx =0;
while(fgets(line, sizeof(line),fptr)!=NULL &&idx<i)
{
puts(line);
char *buf = strtok(line, delim);
int iter =0;
while(buf!=NULL)
{
if(iter = 0){
array[iter] = malloc(sizeof(char)*strlen(buf));
strcpy(array[iter],buf);
buf = strtok(NULL,delim);
}
for(iter=1;iter<4;iter++){
darray[iter] = (int*)malloc(sizeof(char)*strlen(buf));
memcpy(&darray[iter],buf,(sizeof(char)*strlen(buf)));//
buf=strtok(NULL, delim);
}
}
record[idx].name=malloc(sizeof(char)*strlen(array[0]));
strcpy(record[idx].name,array[0]);
record[idx].literature=darray[1];
record[idx].math=darray[2];
record[idx].science=darray[3];
// printf("array value: %s %d %d %d\n",array[0], array[1], array[2], array[3]);
// printf("value after casting: %s %zu %zu %zu\n", array[0], (uintptr_t)array[1], (uintptr_t)array[2], (uintptr_t)array[3]);
//sscanf(buf,"%s %d %d %d",record[idx].name, &record[idx].literature, &record[idx].math, &record[idx].science);
printf("%s\n", line);
//fscanf(fptr,"%s %d %d %d",record[idx].name, &record[idx].literature, &record[idx].math, &record[idx].science);
line[strlen(line)-1]='\0';
printf("in idx loop %d %s %d %d %d\n\n", idx, record[idx].name, record[idx].literature, record[idx].math, record[idx].science);
idx++;
}
//output of file
printf("output commencing\n\n\n");
int tempave;
FILE *fout;
char *str1 = "Name Literature Math Science Ave.\n";
char *str2 = "Ave. ";
char *str3 = "Sarah\t 96\t 90\t 80\t 88.67";
char *str4 = "Minsu\t 55\t 70\t 76\t 67.00";
char *str5 = "Nara\t 88\t 70\t 96\t 84.67";
char *str6 = "79.67 76.67 84.00 80.11";
fout = fopen("output.txt", "w");
fprintf(fout,"%s\n", str1);
for(int idx=0;idx<i;idx++){
tempave = (record[idx].math+record[idx].literature+record[idx].science)/3;
fwrite(&record[idx],sizeof(struct studentT),1,fout);
fprintf(fout, "%d\n",tempave);
}
//fprintf(fout, "%s\n %s\n %s\n %s %s\n",str3,str4,str5,str2,str6);
fprintf(fout,"%s",str2);
float mathave,litave,sciave;
for(int idx=0;idx<3;idx++){
mathave+=record[idx].math;
litave+=record[idx].literature;
sciave+=record[idx].science;
}
mathave=mathave/3;
sciave=sciave/3;
litave=litave/3;
fprintf(fout,"%f %f %f",litave, mathave, sciave);
fclose(fptr);
fclose(fout);
}
I have wrote a new version of what you need with comments. Hopefully, you can understand what is happening and fix your code or you can use this as well. You may need to change the name of the inputfile and outputfile to work with your input file.
#include <stdio.h>
#include <stdlib.h>
struct student {
char *name;
int literature;
int math;
int science;
};
typedef struct student Student;
int main(void) {
FILE* inputFile;
FILE* outputFile;
int first_read; //check to see if the number of students value was read
int readNumStudents; //count of how many students are there
int i; //tracking students struct array
Student* students; //array of student structs
readNumStudents = 0;
first_read = 0;
i = 0;
inputFile = fopen("input.txt", "r"); //might have to change to whatever your file name is
outputFile = fopen("output.txt", "w"); //might have to change to whatever you want the output file name to be
//checking for null file pointers
if(inputFile == NULL){
printf("Failed to open input file.\n");
exit(1);
}
if(outputFile == NULL){
printf("Failed to open output file.\n");
exit(1);
}
//read file until end of file is reached
while(!feof(inputFile)){
if (first_read == 0){// check to see if first line was read. if not read, then write the value to readNumStudents variable
fscanf(inputFile, "%d", &readNumStudents);
first_read = 1; //set the value to 1. this means that the number of students was read.
students = (Student*) malloc(sizeof(Student) * readNumStudents);//allocate Student structs for the number of given students fro input file. (ie. 3 structs)
if(students == NULL){//check for null
printf("Failed to allocate memory for students.\n");
break;
}
//If not null, then for every struct allocate memory for the name string because its char* not char name[50]
for(int j = 0; j < readNumStudents; j++){
students[j].name = (char*) malloc(sizeof(char) * 50);
if(students[j].name == NULL){//check for null
printf("Failed to allocate memory for name.\n");
exit(1);
}
}
} else {//this means that the first line was read. now read the names and scors
//since you know the format of the input file (string int int int) you can use fscanf with "%s %d %d %d" and the adress of the struct values
fscanf(inputFile, "%s %d %d %d", students[i].name, &(students[i].literature), &(students[i].math), &(students[i].science));
i++;//used to point to next student structure
}
}
//Go thorugh every struct and write each value to outputfile
for(int j = 0; j < i; j++){
fprintf(outputFile, "%s %d %d %d\n", students[j].name, (students[j].literature), (students[j].math), (students[j].science));
}
//free all the allocated memory and files
fclose(inputFile);
fclose(outputFile);
//you need to free the char array on top of the studnets struct array. so loop through every student to free it
for(int j = 0; j < i; j++){
free(students[j].name);
}
free(students);
return 0;
}

why fscanf returns a D from a file with context?

This section of my C code is designed to read from a file.
int Id;
char schoolname[10];
char DOB[20];
char gender[300];
char EVENT[20];
char FNAME[20];
char LNAME[20];
if(strcmp(GENDER,"MALE")==0){
FILE *PTR = fopen("./REGISTER/INDIVIUALL_ALL_AROUND_MALE_EVENT.txt", "r+");
if (PTR == NULL) {
printf("Error File 1 Not Created");
} else {
for (int i = 0; i < UNTIL; i++) {
fscanf(PTR, "%d%s%s%s%s%s%s", &Id, &schoolname[0], &DOB[0],
&gender[0] ,&EVENT[0],&FNAME[0],&LNAME[0]);
printf("\n%d\t%s\t\t%s\t%s\t\t%s\t\t%s\t%s", Id, schoolname, DOB,
gender, EVENT, FNAME, LNAME);
}
fclose(PTR);
}
this is the context of my file
My issue is that the when printing gender it returns a D.enter image description here
I have tried, changing the size of each array and even the format in which I scan.

how data can be read from a text file and stored as as a structure?

Let's call this file f1.txt and it has given attributes.
Student code
name
ID
and resident structure from another file let's call f2.txt will be read with the following attributes
ID
City
and residence will be asked from keyboard.
I tried to but gett stucked at some point
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student
{
int student_code;
char name[20];
char ID[20];
};
int main()
{
FILE *input_file;
struct student input;
input_file = fopen("f1.txt", "r");
if(input_file == NULL)
{
fprintf(stderr, "\nError!\n");
exit(1);
}
while(fread(&input, sizeof(struct student), 1, input_file))
printf("student code = %d name = %s ID = %s", input.student_code,
input.name, input.ID);
fclose(input_file);
return 0;
}
I'm new at C programming
for example f1.txt file will be in the following format
f1.txt
123456 yourname 987654
564566 test 454545
Use fscanf to read the lines because you know the format
Store the values into temporary variables
Copy them into a proper data structure: if you don't know the number of students use a dynamic array or a list.
EDIT:
Arrays allow random access, on the other hand lists only allow sequential access.
Here's my attempt using lists:
typedef struct student
{
int student_code;
char name[20];
char ID[20];
struct student* next;
}student_t;
typedef struct list_s
{
int size;
student_t* head;
student_t* tail;
} list_t;
void list_push_back(list_t* l, int code, char n[20], char id[20])
{
student_t* tmp = (student_t*)calloc(1,sizeof(*tmp));
tmp->student_code = code;
strncpy(tmp->name, n, 20);
strncpy(tmp->ID, id, 20);
if (l->head == NULL)
{
l->head = tmp;
l->tail = tmp;
l->size++;
return;
}
l->tail->next = tmp;
l->size++;
}
void list_print(list_t* l)
{
student_t* it = l->head;
while (it)
{
fprintf(stdout, "%d %s %s\n", it->student_code, it->name, it->ID);
it = it->next;
}
}
int main(int argc, char* argv[])
{
FILE *input_file;
struct student input;
input_file = fopen("f1.txt", "r");
if (input_file == NULL)
{
fprintf(stderr, "\nError!\n");
exit(1);
}
list_t* list = (list_t*)calloc(1,sizeof(*list));
char tmp_name[20];
char tmp_id[20];
int tmp_code = 0;
while (fscanf(input_file, "%d %s %s", &tmp_code, tmp_name, tmp_id) != EOF)
{
list_push_back(list, tmp_code, tmp_name, tmp_id);
}
fclose(input_file);
list_print(list);
return 0;
}
To read a single record into the structure input:
fscanf( input_file, "%d %s %s", &input.student_code, input.name, input.ID )
to read a integer and two strings into the relevant members.
fscanf() returns the number of formatted fields successfully assigned, so to display all records in the file in the manner your code attempts:
while( fscanf( input_file, "%d %s %s", &input.student_code,
input.name,
input.ID ) == 3 )
{
printf( "student code = %d, name = %s, ID = %s\n", input.student_code,
input.name,
input.ID ) ;
}

Segmentation fault occurring at very straight-forward part of the code

When I run the program the first time (the output file is not yet created), it works fine. But when I run it again (output file exists), it gives a segmentation fault after reading the last input.
From looking at the problem, it looks like it has something to do with my file handling, but when I tried to debug, I found out that I get the seg. fault before exiting the for loop in main().
#include <stdio.h>
#define EMPS 3
struct employee
{
char firstname[40];
char lastname[40];
int id;
};
typedef struct employee Employee;
/* Input the employee data interactively from the keyboard */
void InputEmployeeRecord(Employee *ptrEmp);
/* Display the contents of a given Employee record */
void PrintEmployeeRecord(const Employee e);
/* Save the contents of the employee record list to the newly
created text file specified by FileName */
void SaveEmployeeRecord(const Employee e[], const char *FileName);
int id = 0;
int main()
{
Employee employees[EMPS];
for(int i = 0; i < EMPS; i++)
{
InputEmployeeRecord(&(employees[i]));
PrintEmployeeRecord(employees[i]);
}
SaveEmployeeRecord(employees, "employee.dat");
return 0;
}
void InputEmployeeRecord(Employee *ptrEmp)
{
printf("Enter first name\n");
scanf("%s", ptrEmp->firstname);
printf("Enter last name\n");
scanf("%s", ptrEmp->lastname);
ptrEmp->id = ++id;
}
void PrintEmployeeRecord(const Employee e)
{
printf("Employee %d: %s %s\n", e.id, e.firstname, e.lastname);
}
void SaveEmployeeRecord(const Employee e[], const char *FileName)
{
FILE* fptr = fopen(FileName, "r");
/* File doesn't exist */
if(fptr == NULL)
{
fclose(fptr);
// Create the file
FILE* fptr2 = fopen(FileName, "w");
fclose(fptr2);
// continue reading
fptr = fopen(FileName, "r");
}
char firstLetter;
int headerExists = 0;
if(!feof( fptr ))
{
fscanf(fptr, "%c", firstLetter);
if(firstLetter == 'I')
headerExists = 1;
}
fclose(fptr);
FILE* fptr2 = fopen(FileName, "a");
if(!headerExists)
fprintf(fptr2, "ID FIRSTNAME LASTNAME");
for(int i = 0; i < EMPS; i++)
fprintf(fptr2, "\n%d %s %s", e[i].id, e[i].firstname, e[i].lastname);
fclose(fptr2);
}
[ Code originally at http://pastebin.com/tLefJhEH ]
The problem probably lies at the fscanf(fptr, "%c", firstLetter);. %c expects a char* as input while you are giving it a char (which is an integer in c).
To fix it try fscanf(fptr, "%c", &firstLetter);
Also calling fclose on a null pointer is not recommended.
My mistake, it's this line:
68. fscanf(fptr, "%c", firstLetter);
Should be:
68. fscanf(fptr, "%c", &firstLetter);
Argments to fscanf and scanf are addresses of the memory locations to store the data.
You don't need the & for a string because a string is already an address.

Resources