简体   繁体   English

在 C 中使用 FREAD 读取文件的问题

[英]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)) 总是错误的;
  • You are misusing scanf ;你在滥用scanf
  • You are not checking the return values of fopen and fread ;您没有检查fopenfread的返回值;
  • The format of your file suggests it is a 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM