Related
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);
}
#include <stdio.h>
struct BirdHome{
char area[500];
char heightcm[100];
char feederquantity[10];
char hasNest[5];
};
struct Bird{
char isRinged[5];
char nameSpecies[50];
char birdAgeMonths[500];
struct BirdHome hom;
char gender[6];
};
struct Bird birds;
int main(void){
FILE *oput;
int max=100;
int count = 0;
char filename[100];
printf("file name? : ");
scanf("%s", &filename);
count = load(filename, &birds, max);
if (count == 0)
printf("No structures loaded\n");
else (
printf("Data loaded\n")
);
save(&birds, oput);
return 0;
}
int load(char *filename, struct Bird *birds, int max){
int count = 0;
FILE *fp = fopen(filename, "r");
char line[100 * 4];
if (fp == NULL)
return 1;
while (count < max && fgets(line, sizeof(line), fp) != NULL){
sscanf(line, "%s %s %s %s %s %s %s %s", birds[count].isRinged, birds[count].nameSpecies,
birds[count].birdAgeMonths, birds[count].hom.area,
birds[count].hom.heightcm, birds[count].hom.feederquantity,
birds[count].hom.hasNest,birds[count].gender);
count++;
}
fclose(fp);
return count;
}
int save (struct Bird *birds, FILE *oput){
int i;
oput=fopen("birdssave.txt","w");
for (i=0;i<3;i++){
fprintf(oput,"%s %s %s %s %s %s %s %s\n",birds[i].isRinged, birds[i].nameSpecies,
birds[i].birdAgeMonths, birds[i].hom.area,
birds[i].hom.heightcm, birds[i].hom.feederquantity,
birds[i].hom.hasNest,birds[i].gender);
}
fclose(oput);
}
Well, the problem was said in the description of the question. Somehow, the load function works properly (at least I think so, because it runs properly and the success message is always displayed) and the save function runs without errors, but it doesn't write the needed info inside a file and just leaves gaps.
True sparrow 3 30 20 2 False Male
False crane 24 200 100 6 True Female
False griffin 14 300 80 1 False Male
This is a text file which my program used to write and load. I think this can somehow help you to find my mistakes in this code.
The load function is made unproperly so it doesn't work. The normal functions does a lot more things to do. Here is the text of it with the needed commentaries
int load(char * filename){
FILE * fp;
char *c;
int m = sizeof(int);
int n, i;
/*prepare memory for info*/
int *pti = (int *)malloc(m);
if ((fp = fopen(filename, "r")) == NULL){
perror("Error occured while opening file");
return 1;
}
/*read the quantity of structures*/
c = (char *)pti;
while (m>0){
i = getc(fp);
if (i == EOF) break;
*c = i;
c++;
m--;
}
/*get the number of elements*/
n = *pti;
/*prepare memory for read massive of structures*/
struct bird * ptr = (struct bird *) malloc(3 * sizeof(struct bird));
c = (char *)ptr;
/*after save we read massive by symbols*/
while ((i= getc(fp))!=EOF){
*c = i;
c++;
}
/*sort the elements and printing in console*/
printf("\n%d birds in the file stored\n\n", n);
for (int k = 0; k<n; k++){
printf("%d %s %s %d %d %d %d %s %s \n", k + 1,
ptr[k].isRinged,ptr[k].nameSpecies,ptr[k].birdAgeMonths,ptr[k].homearea,
ptr[k].homeheightcm,ptr[k].homefeederquantity,ptr[k].homehasNest,ptr[k].gender);
}
free(pti);
free(ptr);
fclose(fp);
return 0;
}
At least, the save function can be untouched only because algorhytmically it does the same as the normal code.
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;
}
When execute second fscanf, console stop working. What did I do wrong?
The input file contains:
3
minsu 50 80 40
sarah 30 60 40
jason 70 80 90
The code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef struct studentT {
char *name;
int literature;
int math;
int science;
}studentT;
int main()
{
studentT student[3];
int count;
char *inputFileName = malloc(sizeof(char)*30);
char *outputFileName = malloc(sizeof(char) * 30);
float avg;
int i = 0;
scanf("%s %s", inputFileName, outputFileName);
FILE *fp = fopen(inputFileName, "r");
if (fp == NULL)
{
printf("file is not exist");
return 1;
}
fscanf(fp, "%d", &count);
for (i = 0; i < (int)count; i++)
{
//printf("!");
fscanf(fp, "%s %d %d %d", student[i].name, &student[i].literature, &student[i].math, &student[i].science);
//printf("2");
printf("%s %d %d %d\n", student[i].name, student[i].literature, student[i].math, student[i].science);
//printf("333\n");
}
fclose(fp);
free(inputFileName);
free(outputFileName);
return 0;
}
The name field in your studentT struct is a char *. You pass that pointer to scanf without initializing it to anything. So scanf reads an uninitialized pointer and tried to dereference it. This invokes undefined behavior.
The simplest way to fix this is to change name to be an array large enough to hold any string you expect. Then you can write to the array:
typedef struct studentT {
char name[20];
int literature;
int math;
int science;
}studentT;
Alternately, you can use malloc to allocate space dynamically:
student[i].name = malloc(20);
fscanf(fp, "%19s %d %d %d", student[i].name, &student[i].literature,
&student[i].math, &student[i].science);
I have this file with the following contents:
Bob Human, 1
John Cat, 3
Mary Dog, 2
How can I properly use fscanf to have each string and integer in a struct.
typedef struct {
char name[20];
char surname[20];
int code;
} entry;
Then I create an array of _entry_
entry a[3];
How will _a_ get each value properly using fscanf?
EDIT :
I have tried this:
while(TRUE) {
nscan=fscanf(infile, "%s %s d%c", temp.name, temp.surname, &temp.code, &termch);
if(nscan==EOF) break;
if(nscan!=4 || termch!='\n') {
printf("Error\n");
}
RecBSTInsert(&a, temp);
}
But it seems to pass the last line twice.
You're close, but you're not handling the comma properly.
As usual, it's much easier to read whole lines, then parse them. So let's do that.
Try:
char line[1024];
if(fgets(line, sizeof line, infile) != NULL)
{
nscan = sscanf(line, "%s %[^,], %d", temp.name, temp.surname, &temp.code);
}
The return value will be 3 if all the fields converted, else you have an error.
#include <stdio.h>
typedef struct{
char name[20];
char surname[20];
int code;
} entry;
int main(){
entry temp, a[3];
FILE *infile = fopen("data.txt", "r");
int i=0, n;
while(fscanf(infile, "%19s %19[^,], %d", temp.name, temp.surname, &temp.code)==3){
a[i++] = temp;
if(i==3)break;
}
fclose(infile);
n = i;
for(i=0;i<n;++i){
printf("%s %s, %d\n", a[i].name, a[i].surname, a[i].code);
}
return 0;
}