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
enur.yilmaz@tedu.edu.tr
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 : enur.yilmaz@tedu.edu.tr
Successfully read the following student entry:
Number : 10000000010
Name : FEYZA NUR DUMAN
Department: Computer Engineering
Unknown : 2
E-Mail : fnur.duman@tedu.edu.tr
Successfully read the following student entry:
Number : 20000000010
Name : GOKHAN YAMAC
Department: Computer Engineering
Unknown : 2
E-Mail : gokhan.yamac@tedu.edu.tr
Successfully read the following student entry:
Number : 30000000030
Name : CEREN AYDINLI
Department: Computer Engineering
Unknown : 2
E-Mail : ceren.aydinli@tedu.edu.tr
Successfully read the following student entry:
Number : 30000000010
Name : DURU YAMAC
Department: Computer Engineering
Unknown : 3
E-Mail : duru.yamac@tedu.edu.tr
Successfully read the following student entry:
Number : 40000000010
Name : SEVIL TERZI
Department: Computer Engineering
Unknown : 2
E-Mail : sevil.terzi@tedu.edu.tr
Successfully read the following student entry:
Number : 50000000010
Name : EREN AYDIN
Department: Computer Engineering
Unknown : 2
E-Mail : eren.aydin@tedu.edu.tr
Successfully read the following student entry:
Number : 50000000020
Name : YAMAC YILMAZ
Department: Computer Engineering
Unknown : 2
E-Mail : yamac.yilmaz@tedu.edu.tr
Successfully read the following student entry:
Number : 60000000020
Name : EDANUR YILMAZ
Department: Computer Engineering
Unknown : 2
E-Mail : edanur.yilmaz@tedu.edu.tr
Successfully read the following student entry:
Number : 70000000010
Name : GOKHAN YAMAC
Department: Computer Engineering
Unknown : 2
E-Mail : gokhan.yamac18@tedu.edu.tr
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
);
}
}