[英]Problem with reading a file by using FREAD into struct in C
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;enur.yilmaz@tedu.edu.tr;
10000000010;FEYZA NUR DUMAN;Computer Engineering;2;fnur.duman@tedu.edu.tr;
20000000010;GOKHAN YAMAC;Computer Engineering;2;gokhan.yamac@tedu.edu.tr;
30000000030;CEREN AYDINLI;Computer Engineering;2;ceren.aydinli@tedu.edu.tr;
30000000010;DURU YAMAC;Computer Engineering;3;duru.yamac@tedu.edu.tr;
40000000010;SEVIL TERZI;Computer Engineering;2;sevil.terzi@tedu.edu.tr;
50000000010;EREN AYDIN;Computer Engineering;2;eren.aydin@tedu.edu.tr;
50000000020;YAMAC YILMAZ;Computer Engineering;2;yamac.yilmaz@tedu.edu.tr;
60000000020;EDANUR YILMAZ;Computer Engineering;2;edanur.yilmaz@tedu.edu.tr;
70000000010;GOKHAN YAMAC;Computer Engineering;2;gokhan.yamac18@tedu.edu.tr;
OUTPUT OF MY CODE IS:我的代码的输出是:
Ogrenci No;Ogrenci;Program;Sinif;Email;Status
10000000000;EDA NUR YILMAZ;Computer Engineering;4;enPI_K³Æ_aj©ur.yilmaz@tedu.edu.tr;
10000000010;FEYZA NUR DUMAN;Computer Engineering;2;fnur.duman@tedu.edu.tr;
PI_K³Æ_aj©20000000010;GOKHAN YAMAC;Computer Engineering;2;gokhan.yamac@tedu.edu.tr;
30000000030;CEREN AYDINLPI_K³Æ_aj©I;Computer Engineering;2;ceren.aydinli@tedu.edu.tr;
30000000010;DURU YAMAC;Computer Engineering;3;dPI_K³Æ_aj©uru.yamac@tedu.edu.tr;
40000000010;SEVIL TERZI;Computer Engineering;2;sevil.terzi@tedu.edu.tr;
50000PI_K³Æ_aj©000010;EREN AYDIN;Computer Engineering;2;eren.aydin@tedu.edu.tr;
50000000020;YAMAC YILMAZ;ComputPI_K³Æ_aj©er Engineering;2;yamac.yilmaz@tedu.edu.tr;
60000000020;EDANUR YILMAZ;Computer Engineering;2;edanuPI_K³Æ_aj©r.yilmaz@tedu.edu.tr;
70000000010;GOKHAN YAMAC;Computer Engineering;2;gokhan.yamac18@tedu.edu.tr;
nuPI_K³Æ_aj©
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?
有关详细信息,请参阅以下问题:为什么“while (!feof (file))”总是错误的?
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.将尝试从文件中准确读取
sizeof(struct StudentInfo)
个字节,即 100 个字节。 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.换句话说,您的程序假设每一行正好是 100 字节长,并且下一个条目也正好是 100 字节长。 Your program is also assuming that there is nothing in between these 100 byte long entries (so also no newline character).
您的程序还假设在这些 100 字节长的条目之间没有任何内容(因此也没有换行符)。 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
;
条目的字段是可变长度的(不是 20 的固定长度),它们之间用
;
分隔。 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:因此,使用函数
fgets
一次读取一行并使用strtok
将行划分为各个字段会更合适:
#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
.由于您还没有回复我关于输入字段中第四个字段含义的澄清请求,我只是在
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:这是因为您对
struct StudentInfo
的定义包含以下行:
char department[20];
This means that it is only able to store 19
characters plus the terminating null character.这意味着它只能存储
19
字符加上终止的空字符。 However, in the input file, you have Computer Engineering
, which is 20
characters long ( 21
including the terminating null character).但是,在输入文件中,您有
Computer Engineering
,它有20
字符长( 21
个字符包括终止的空字符)。 Therefore, you must increase the size of the array to at least 21
.因此,您必须将数组的大小至少增加到
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)在输入文件中,因为它需要 24 个字符(包括终止字符)
After increasing the size of both arrays from 20
to 30
, the program will work.将两个数组的大小从
20
增加到30
后,程序将运行。 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:
处理所有 5 个字段的代码几乎相同,因此最好统一此代码:
#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
);
}
}
Your code presents many problems:您的代码存在许多问题:
char operationType[2];
can only hold one character (the other one is for \0
);\0
);struct StudentInfo *studentInfo=(struct StudentInfo*) malloc(sizeof(struct StudentInfo));
is too unnecessarily long;while(!feof(firstFile))
is always wrong ; while(!feof(firstFile))
总是错误的;scanf
;scanf
;fopen
and fread
;fopen
和fread
的返回值;csv
with a header.csv
。 In your case, fread
is not the proper way to read your file into struct StudentInfo
.fread
不是将文件读入struct StudentInfo
的正确方法。 fgets
is more appropriate. fgets
更合适。 @Andreas Wenzel already provided a solution . @Andreas Wenzel已经提供了一个解决方案。 The following is another version using
sscanf
:以下是使用
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
:然后结合
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, " %11[^;\n];%49[^;\n];%49[^;\n];%d;%49[^;\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 %11[^;\n];
在这里,
%11[^;\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." part 的意思是“读取所有内容,直到找到换行符或分号,或者达到 11 个字符的最大限制。然后读取分号。”
Output:输出:
Student #1
10000000000
EDA NUR YILMAZ
Computer Engineering
4
enur.yilmaz@tedu.edu.tr
Student #2
10000000010
FEYZA NUR DUMAN
Computer Engineering
2
fnur.duman@tedu.edu.tr
Student #3
20000000010
GOKHAN YAMAC
Computer Engineering
2
gokhan.yamac@tedu.edu.tr
Student #4
30000000030
CEREN AYDINLI
Computer Engineering
2
ceren.aydinli@tedu.edu.tr
Student #5
30000000010
DURU YAMAC
Computer Engineering
3
duru.yamac@tedu.edu.tr
Student #6
40000000010
SEVIL TERZI
Computer Engineering
2
sevil.terzi@tedu.edu.tr
Student #7
50000000010
EREN AYDIN
Computer Engineering
2
eren.aydin@tedu.edu.tr
Student #8
50000000020
YAMAC YILMAZ
Computer Engineering
2
yamac.yilmaz@tedu.edu.tr
Student #9
60000000020
EDANUR YILMAZ
Computer Engineering
2
edanur.yilmaz@tedu.edu.tr
Student #10
70000000010
GOKHAN YAMAC
Computer Engineering
2
gokhan.yamac18@tedu.edu.tr
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.