I wrote the code below to read a file into a structure but the output when I run this code can be seen in the text below. I want to read the file into the structure with its each char block like number, name, surname.. Original file is also like below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
struct StudentInfo{
char studentNumber[20];
char studentName[20];
char studentSurname[20];
char department[20];
char eMail[20];
};
int main(int argc, char *argv[]) {
//Declarations
char firstFileName[20];
char secondFileName[20];
char operationType[2];
char data[100];
struct StudentInfo *studentInfo=(struct StudentInfo*) malloc(sizeof(struct StudentInfo));
FILE *firstFile;
FILE *secondFile;
printf("Enter the first file name: ");
scanf("%s",firstFileName);
printf("Enter the second file name: ");
scanf("%s",secondFileName);
printf("Enter the operation type (-i, -u):");
scanf("%s",operationType);
if(strcmp(operationType,"-i")==0){
firstFile = fopen(strcat(firstFileName,".txt"),"r");
while(!feof(firstFile)){
fread(studentInfo,sizeof(struct StudentInfo),1,firstFile);
printf("%s",studentInfo->studentNumber);
}
}
if(strcmp(operationType,"-u")==0){
printf("%s",secondFileName);
}
fclose(firstFile);
/*
while(!feof(firstFile)){
students[1]->studentNumber =
}
fclose(firstFile);*/
}
ORIGINAL CONTENT OF THE FILE IS:
Ogrenci No;Ogrenci;Program;Sinif;Email;Status
10000000000;EDA NUR YILMAZ;Computer Engineering;4;[email protected];
10000000010;FEYZA NUR DUMAN;Computer Engineering;2;[email protected];
20000000010;GOKHAN YAMAC;Computer Engineering;2;[email protected];
30000000030;CEREN AYDINLI;Computer Engineering;2;[email protected];
30000000010;DURU YAMAC;Computer Engineering;3;[email protected];
40000000010;SEVIL TERZI;Computer Engineering;2;[email protected];
50000000010;EREN AYDIN;Computer Engineering;2;[email protected];
50000000020;YAMAC YILMAZ;Computer Engineering;2;[email protected];
60000000020;EDANUR YILMAZ;Computer Engineering;2;[email protected];
70000000010;GOKHAN YAMAC;Computer Engineering;2;[email protected];
OUTPUT OF MY CODE IS:
Ogrenci No;Ogrenci;Program;Sinif;Email;Status
10000000000;EDA NUR YILMAZ;Computer Engineering;4;enPI_K³Æ_aj©[email protected];
10000000010;FEYZA NUR DUMAN;Computer Engineering;2;[email protected];
PI_K³Æ_aj©20000000010;GOKHAN YAMAC;Computer Engineering;2;[email protected];
30000000030;CEREN AYDINLPI_K³Æ_aj©I;Computer Engineering;2;[email protected];
30000000010;DURU YAMAC;Computer Engineering;3;dPI_K³Æ_aj©[email protected];
40000000010;SEVIL TERZI;Computer Engineering;2;[email protected];
50000PI_K³Æ_aj©000010;EREN AYDIN;Computer Engineering;2;[email protected];
50000000020;YAMAC YILMAZ;ComputPI_K³Æ_aj©er Engineering;2;[email protected];
60000000020;EDANUR YILMAZ;Computer Engineering;2;edanuPI_K³Æ_aj©[email protected];
70000000010;GOKHAN YAMAC;Computer Engineering;2;[email protected];
nuPI_K³Æ_aj©
CodePudding user response:
The loop
while(!feof(firstFile)){
fread(studentInfo,sizeof(struct StudentInfo),1,firstFile);
printf("%s",studentInfo->studentNumber);
}
is wrong, for several reasons:
The loop condition is wrong. See the following question for further information: Why is “while ( !feof (file) )” always wrong?
The line
fread(studentInfo,sizeof(struct StudentInfo),1,firstFile);
will attempt to read exactly sizeof(struct StudentInfo)
bytes, which is 100 bytes, from the file. In other words, your program is assuming that every line is exactly 100 bytes long, and that the next entry is also exactly 100 bytes long. Your program is also assuming that there is nothing in between these 100 byte long entries (so also no newline character). Additionally, your program is assuming that every string in the file has a null terminating character in it (because you are later attempting to print them as null-terminated strings). All three of these assumptions are wrong.
What you actually have is a file in which each entry is separated by a newline character. The fields of the entries are of variable length (not a fixed length of 20), which are separated by ;
characters. Therefore, it would be more appropriate to read one line at a time using the function fgets
, and to use strtok
to divide the line into its individual fields:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct StudentInfo
{
char studentNumber[20];
char studentName[20];
char department[20];
char unknown[20];
char eMail[20];
};
int main( void )
{
FILE *fpInput;
char line[400];
struct StudentInfo si;
//open input file
fpInput = fopen( "input.txt", "r" );
if ( fpInput == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//ignore first line of input
fgets( line, sizeof line, fpInput );
//read one line of input per loop iteration
while ( fgets( line, sizeof line, fpInput ) != NULL )
{
char *p;
//attempt to find newline character
p = strchr( line, '\n' );
//make sure entire line was read, and remove newline
//character if necessary
if ( p == NULL )
{
//a missing newline character is ok on end-of-file
if ( !feof(fpInput) )
{
fprintf( stderr, "Line too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character from input
*p = '\0';
}
//attempt to read student number
p = strtok( line, ";" );
if ( p == NULL )
{
fprintf( stderr, "Unable to find student number!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy student number to struct
if ( snprintf( si.studentNumber, sizeof si.studentNumber, "%s", p ) >= (int)(sizeof si.studentNumber) )
{
fprintf( stderr, "Not enough space to copy student number.\n" );
exit( EXIT_FAILURE );
}
//attempt to read student name
p = strtok( NULL, ";" );
if ( p == NULL )
{
fprintf( stderr, "Unable to find student name!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy student name to struct
if ( snprintf( si.studentName, sizeof si.studentName, "%s", p ) >= (int)(sizeof si.studentName) )
{
fprintf( stderr, "Not enough space to copy student name.\n" );
exit( EXIT_FAILURE );
}
//attempt to read department
p = strtok( NULL, ";" );
if ( p == NULL )
{
fprintf( stderr, "Unable to find department!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy department to struct
if ( snprintf( si.department, sizeof si.department, "%s", p ) >= (int)(sizeof si.department) )
{
fprintf( stderr, "Not enough space to copy department.\n" );
exit( EXIT_FAILURE );
}
//attempt to read unknown field
p = strtok( NULL, ";" );
if ( p == NULL )
{
fprintf( stderr, "Unable to find unknown field!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy unknown field to struct
if ( snprintf( si.unknown, sizeof si.unknown, "%s", p ) >= (int)(sizeof si.unknown) )
{
fprintf( stderr, "Not enough space to copy unknown field.\n" );
exit( EXIT_FAILURE );
}
//attempt to read email
p = strtok( NULL, ";" );
if ( p == NULL )
{
fprintf( stderr, "Unable to find email!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy email to struct
if ( snprintf( si.eMail, sizeof si.eMail, "%s", p ) >= (int)(sizeof si.eMail) )
{
fprintf( stderr, "Not enough space to copy eMail.\n" );
exit( EXIT_FAILURE );
}
//print data from struct
printf(
"Successfully read the following student entry:\n"
"Number : %s\n"
"Name : %s\n"
"Department: %s\n"
"Unknown : %s\n"
"E-Mail : %s\n"
"\n",
si.studentNumber, si.studentName, si.department,
si.unknown, si.eMail
);
}
}
Since you have not yet responded to my request for clarification on what the meaning of the fourth field in the input field is, I am simply labelling it as "unknown" in stuct StudentInfo
.
However, when I run this program, I get the following error message:
Not enough space to copy department.
This is because your definition of struct StudentInfo
contains the following line:
char department[20];
This means that it is only able to store 19
characters plus the terminating null character. However, in the input file, you have Computer Engineering
, which is 20
characters long (21
including the terminating null character). Therefore, you must increase the size of the array to at least 21
.
You have the same problem with
char eMail[20];
This array is not large enough to store the string
[email protected]
in the input file, because it requires 24 characters (including the terminating character)
After increasing the size of both arrays from 20
to 30
, the program will work. It will have the following output:
Successfully read the following student entry:
Number : 10000000000
Name : EDA NUR YILMAZ
Department: Computer Engineering
Unknown : 4
E-Mail : [email protected]
Successfully read the following student entry:
Number : 10000000010
Name : FEYZA NUR DUMAN
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 20000000010
Name : GOKHAN YAMAC
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 30000000030
Name : CEREN AYDINLI
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 30000000010
Name : DURU YAMAC
Department: Computer Engineering
Unknown : 3
E-Mail : [email protected]
Successfully read the following student entry:
Number : 40000000010
Name : SEVIL TERZI
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 50000000010
Name : EREN AYDIN
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 50000000020
Name : YAMAC YILMAZ
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 60000000020
Name : EDANUR YILMAZ
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
Successfully read the following student entry:
Number : 70000000010
Name : GOKHAN YAMAC
Department: Computer Engineering
Unknown : 2
E-Mail : [email protected]
However, one thing that is not so nice about this solution is that it contains a significant amount of code duplication. The code for handling all 5 fields is nearly identical, so it would be better to unify this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct StudentInfo
{
char studentNumber[20];
char studentName[20];
char department[30];
char unknown[20];
char eMail[30];
};
int main( void )
{
FILE *fpInput;
char line[400];
struct StudentInfo si;
struct field
{
char *p_str;
size_t size;
};
//arrange fields in an array, so that it can be used in
//a loop
struct field fields[] = {
{ si.studentNumber, sizeof si.studentNumber },
{ si.studentName, sizeof si.studentName },
{ si.department, sizeof si.department },
{ si.unknown, sizeof si.unknown },
{ si.eMail, sizeof si.eMail }
};
//open input file
fpInput = fopen( "input.txt", "r" );
if ( fpInput == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//ignore first line of input
fgets( line, sizeof line, fpInput );
//read one line of input per loop iteration
while ( fgets( line, sizeof line, fpInput ) != NULL )
{
char *p;
//attempt to find newline character
p = strchr( line, '\n' );
//make sure entire line was read, and remove newline
//character if necessary
if ( p == NULL )
{
//a missing newline character is ok on end-of-file
if ( !feof(fpInput) )
{
fprintf( stderr, "Line too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character from input
*p = '\0';
}
p = strtok( line, ";" );
for ( size_t i = 0; i < sizeof fields / sizeof *fields; i )
{
//verify that field exists
if ( p == NULL )
{
fprintf( stderr, "Unable to find student number!\n" );
exit( EXIT_FAILURE );
}
//attempt to copy field to struct
if ( snprintf( fields[i].p_str, fields[i].size, "%s", p ) >= (int)(fields[i].size) )
{
fprintf( stderr, "Not enough space to copy field.\n" );
exit( EXIT_FAILURE );
}
p = strtok( NULL, ";" );
}
//print data from struct
printf(
"Successfully read the following student entry:\n"
"Number : %s\n"
"Name : %s\n"
"Department: %s\n"
"Unknown : %s\n"
"E-Mail : %s\n"
"\n",
si.studentNumber, si.studentName, si.department,
si.unknown, si.eMail
);
}
}
CodePudding user response:
Your code presents many problems:
char operationType[2];
can only hold one character (the other one is for\0
);struct StudentInfo *studentInfo=(struct StudentInfo*) malloc(sizeof(struct StudentInfo));
is too unnecessarily long;while(!feof(firstFile))
is always wrong;- You are misusing
scanf
; - You are not checking the return values of
fopen
andfread
; - The format of your file suggests it is a
csv
with a header. In your case,fread
is not the proper way to read your file intostruct StudentInfo
.fgets
is more appropriate.
@Andreas Wenzel already provided a solution. The following is another version using sscanf
:
First, redefine your struct so it makes more sense:
struct StudentInfo {
char studentNumber[12];
char studentName[50];
char department[50];
int weired; // What is this?
char eMail[50];
};
Second, define a function that reads one line from an input stream:
char *read_line(char *line, size_t size, FILE *stream)
{
if(!fgets(line, size, stream))
return NULL;
size_t npos = strcspn(line, "\n");
line[npos] = '\0';
return line;
}
Then use it in combination of sscanf
:
int main(int argc, const char *argv[])
{
const char *filename = "file.txt";
FILE *file = fopen(filename, "r");
if (!file) {
fprintf(stderr, "Could not open file %s\n", filename);
exit(EXIT_FAILURE);
}
char line[1024]; // 1KB should be large enough
struct StudentInfo students[100];
size_t nstudents = 0;
// Read and discard the first line
read_line(line, sizeof line, file);
// Read the rest of the file
for (size_t lineno = 0; read_line(line, sizeof line, file); lineno) {
char studentNumber[12];
char studentName[50];
char department[50];
int weired;
char eMail[50];
int ret = sscanf(line, " [^;\n];I[^;\n];I[^;\n];%d;I[^;\n];", studentNumber, studentName, department, &weired, eMail);
if (ret != 5) {
printf("> [ERROR] on line %ld: expected %d fields, but only parsed %d\n", lineno, 5, ret);
continue;
}
// Copy data to struct array only when parsing succeeded
struct StudentInfo *st = &students[nstudents ];
strncpy(st->studentNumber, studentNumber, sizeof(studentNumber));
strncpy(st->studentName, studentName, sizeof(studentName));
strncpy(st->department, department, sizeof(department));
st->weired = weired;
strncpy(st->eMail, eMail, sizeof(eMail));
//printf("%s\n%s\n%s\n%d\n%s\n\n", studentNumber, studentName, department, weired, eMail);
}
fclose(file);
for (size_t i = 0; i < nstudents; i) {
struct StudentInfo *st = &students[i];
printf("Student #%ld\n", i 1);
printf("%s\n%s\n%s\n%d\n%s\n\n", st->studentNumber, st->studentName, st->department, st->weired, st->eMail);
}
}
Here, the [^;\n];
part means "read everything until a newline or a semicolon is found, or the maximum limit of 11 characters is hit. Then read a semicolon."
Output:
Student #1
10000000000
EDA NUR YILMAZ
Computer Engineering
4
[email protected]
Student #2
10000000010
FEYZA NUR DUMAN
Computer Engineering
2
[email protected]
Student #3
20000000010
GOKHAN YAMAC
Computer Engineering
2
[email protected]
Student #4
30000000030
CEREN AYDINLI
Computer Engineering
2
[email protected]
Student #5
30000000010
DURU YAMAC
Computer Engineering
3
[email protected]
Student #6
40000000010
SEVIL TERZI
Computer Engineering
2
[email protected]
Student #7
50000000010
EREN AYDIN
Computer Engineering
2
[email protected]
Student #8
50000000020
YAMAC YILMAZ
Computer Engineering
2
[email protected]
Student #9
60000000020
EDANUR YILMAZ
Computer Engineering
2
[email protected]
Student #10
70000000010
GOKHAN YAMAC
Computer Engineering
2
[email protected]