【问题标题】:How to dynamically allocate struct fields based on data read from binary file?如何根据从二进制文件读取的数据动态分配结构字段?
【发布时间】:2017-12-17 14:23:55
【问题描述】:

我的结构看起来像这样:

typedef struct Student {    //dynamically allocated structure
    char *first_name;
    char *last_name;
    float grade;
}Student;

我想做的是从二进制文件中读取数据并为 first_namelast_name 字段动态分配内存。从普通 .txt 文件中读取时,我成功完成了这项任务。代码如下:

void Read_From_File(const char *file_name, Student *p_destination, const int number_of_students)
{
    FILE *p_file = fopen(file_name, "r");
    if (!p_file)
    {
        perror("Error opening the file. \n");
        exit(EXIT_FAILURE);
    }
    int index = 0;
    while (index < number_of_students)
    {
        unsigned char buffer[256];
        fscanf (p_file, "%s", buffer);
        p_destination[index].last_name = (char*)malloc(strlen(buffer) + 1);
        strcpy(p_destination[index].last_name, buffer);

        fscanf(p_file, "%s", buffer);
        p_destination[index].first_name = (char*)malloc(strlen(buffer) + 1);
        strcpy(p_destination[index].first_name, buffer);

        fscanf(p_file, "%f", &p_destination[index].grade);
        index++;
    }
    fclose(p_file);
}

这很简单,因为 fscanf 可以单独读取数据并具有类型说明符,如下所示:%s 姓 / %s 名字 / %f 级。我不知道如何像使用 fscanf 那样使用 fread 读取单个数据。 这是我迄今为止尝试过的,但它不起作用:

int number_of_students = 0;
unsigned char buffer[256];
char file_name[10];
FILE *p_file = NULL;
Student *p_students = NULL;

printf("Enter file name. \n");
scanf("%s", file_name);

p_file = fopen(file_name, "rb");

fread(&number_of_students, sizeof(number_of_students), 1, p_file);
p_students = (Student *)malloc(number_of_students * sizeof(Student));
if (!p_students)
{
    perror("Could not allocate memory. \n");
    exit(EXIT_FAILURE);
}

for (int index = 0; index < number_of_students; ++index)
{
    fread(buffer, sizeof(char), 1, p_file);
    p_students[index].last_name = (char *)malloc(strlen(buffer) + 1);
    if (!p_students[index].last_name)
    {
        printf("Could not allocate memory. \n");
        exit(EXIT_FAILURE);
    }
    strcpy(p_students[index].last_name, buffer);

    fread(buffer, sizeof(char), 1, p_file);
    p_students[index].first_name = (char *)malloc(strlen(buffer) + 1);
    if (!p_students[index].first_name)
    {
        printf("Could not allocate memory. \n");
        exit(EXIT_FAILURE);
    }
    strcpy(p_students[index].first_name, buffer);

    fread(&p_students[index].grade, sizeof(float), 1, p_file);
}
Print_Students(p_students, number_of_students);

另外,Print_Students 函数如下所示:

void Print_Students(Student *p_students, int number_of_students)
{
    printf("================================================================================================\n");
    for (unsigned int index = 0; index < number_of_students; ++index)
    {
        printf("\t\t===================== Student %d =====================\n\n", index);
        printf("Last name: %s\n", p_students[index].last_name);
        printf("First name: %s\n", p_students[index].first_name);
        printf("Grade: %.2f\n", p_students[index].grade);
    }
    printf("\n");
}

input.txt 文件如下所示:

19
Terresa Minaya 9.50
Otto Pleiman 7 
Illa Holzman 5.25
Alona Greeson 3.40
Natalya Vickrey 10
Catrina Cho 9.34
Loida Dinapoli 9.43
Neely Mcelligott 8.30
Salome Urban 8.75
Rosetta Dagenhart 9.10
Diane Cooksey 10
Novella Longmire 3
Gilberte Manganaro 4
Joye Humbert 5
Justa Larock 6
Delana Bufkin 7
Genaro Kenison 8.98
Refugio Arena 4.56
Iona Nida 7.65

我将此文件中的数据加载到我的学生向量中,然后根据我的向量中的数据编写 input.bin 文件。我使用以下代码写入二进制文件:

void Write_To_Binary(const char *file_name, Student *p_source, const int number_of_students)
{   
    size_t successfully_written = 0;
    FILE *p_file = fopen(file_name, "wb");
    if (!p_file)
    {
        perror("Error opening the file. \n");
        exit(EXIT_FAILURE);
    }
    successfully_written = fwrite(&number_of_students, sizeof(number_of_students), 1, p_file);
    if (successfully_written != 1)
    {
        perror("Error writing to the file. \n");
        exit(EXIT_FAILURE);
    }
    successfully_written = fwrite(p_source, sizeof(Student), number_of_students, p_file);
    if (successfully_written != number_of_students)
    {
        perror("Error writing all the student data. \n");
        exit(EXIT_FAILURE);
    }
    fclose(p_file);
}

更新:我遵循了 iBug 的解决方案并且它有效。我将数据字段更改如下: sizeof(last_name) last_name sizeof(first_name) first_name grade.

我将在这里发布我用来写入 bin 文件并从中读取的函数。

这里是函数Write_To_Binary

    void Write_To_Binary(const char *file_name, Student *p_source, const unsigned int number_of_students)
{
    FILE *p_file = fopen(file_name, "wb");
    if (!p_file)
    {
        perror("Error opening the file. \n");
        exit(EXIT_FAILURE);
    }

    size_t successfully_written = 0;
    successfully_written = fwrite(&number_of_students, sizeof(number_of_students), 1, p_file);
    if (successfully_written != 1)
    {
        printf("Error writing to the file. \n");
        exit(EXIT_FAILURE);
    }

    unsigned int width = 0, width1 = 0;
    for (unsigned int index = 0; index < number_of_students; ++index)
    {
        width = strlen(p_source[index].last_name) + 1;

        successfully_written = fwrite(&width, sizeof(width), 1, p_file);
        printf("Name: %s Width: %d \n", p_source[index].last_name, width);
        if (successfully_written != 1)
        {
            printf("Error writing to the file. \n");
            exit(EXIT_FAILURE);
        }

        successfully_written = fwrite(p_source[index].last_name, width, 1, p_file);
        if (successfully_written != 1)
        {
            printf("Error writing to the file. \n");
            exit(EXIT_FAILURE);
        }

        width = strlen(p_source[index].first_name) + 1;

        successfully_written = fwrite(&width, sizeof(width), 1, p_file);
        if (successfully_written != 1)
        {
            printf("Error writing to the file. \n");
            exit(EXIT_FAILURE);
        }

        successfully_written = fwrite(p_source[index].first_name, width, 1, p_file);
        if (successfully_written != 1)
        {
            printf("Error writing to the file. \n");
            exit(EXIT_FAILURE);
        }

        successfully_written = fwrite(&p_source[index].grade, sizeof(float), 1, p_file);
        if (successfully_written != 1)
        {
            printf("Error writing to the file. \n");
            exit(EXIT_FAILURE);
        }
    }

    fclose(p_file);
}

这里是函数Read_From_Binary

void Read_From_Binary(const char *file_name, Student *p_destination)
{
    FILE *p_file = fopen(file_name, "rb");
    if (!p_file)
    {
        perror("Error opening file. \n");
        exit(EXIT_FAILURE);
    }

    unsigned int number_of_students = 0;
    size_t successfully_read;
    successfully_read = fread(&number_of_students, sizeof(number_of_students), 1, p_file);
    if (successfully_read != 1)
    {
        printf("Error reading the number of students. \n");
        exit(EXIT_FAILURE);
    }

    unsigned int width = 0;
    for (unsigned int index = 0; index < number_of_students; ++index)
    {
        successfully_read = fread(&width, sizeof(width), 1, p_file);
        if (successfully_read != 1)
        {
            printf("Error reading from the file. \n");
            exit(EXIT_FAILURE);
        }

        p_destination[index].last_name = (char*)malloc(width);
        if (!p_destination[index].last_name)
        {
            printf("Could not allocate memory. \n");
            exit(EXIT_FAILURE);
        }
        successfully_read = fread(p_destination[index].last_name, width, 1, p_file);
        if (successfully_read != 1)
        {
            printf("Error reading from the file. \n");
            exit(EXIT_FAILURE);
        }

        successfully_read = fread(&width, sizeof(width), 1, p_file);
        if (successfully_read != 1)
        {
            printf("Error reading from the file. \n");
            exit(EXIT_FAILURE);
        }

        p_destination[index].first_name = (char*)malloc(width);
        if (!p_destination[index].first_name)
        {
            printf("Could not allocate memory. \n");
            exit(EXIT_FAILURE);
        }
        successfully_read = fread(p_destination[index].first_name, width, 1, p_file);
        if (successfully_read != 1)
        {
            printf("Error reading from the file. \n");
            exit(EXIT_FAILURE);
        }

        successfully_read = fread(&p_destination[index].grade, sizeof(float), 1, p_file);
        if (successfully_read != 1)
        {
            printf("Error reading from the file. \n");
            exit(EXIT_FAILURE);
        }
    }

    fclose(p_file);
}

【问题讨论】:

  • 我看到一个可能的问题:strlen(buffer) 不是你需要分配的大小,你需要 strlen(buffer)*sizeof(char)
  • @Angen sizeof(char) 在 C 中是 1(按语言规范),所以不是。
  • 感谢您的回答,但它也不起作用。
  • fread(buffer, sizeof(char), 1, p_file);第二个参数是一个元素的大小,第三个参数是要读取的元素数,所以你告诉读取一个大小为 char 的元素
  • 谢谢,我可以看到错误,但我该如何处理呢?二进制文件中的名称是随机的,我无法预测这些名称的长度。假设我需要将 sizeof(char) 替换为 strlen(last_name) * sizeof(char)。

标签: c


【解决方案1】:

我在最近的一个项目中做了a similar job

我自己实现了一个二进制文件格式,它的结构是

MAGIC
number of records
record 1
    (int) length of this record
    data
record 2
    (int) length of this record
    data

所以你可以多坑一个4个字节(或者一个整数)来表示要读什么的长度,按照那个数字来读。

这种实现的优点是:

  • 您不需要先读取另一个缓冲区,这可能会增加对记录长度的额外限制。
  • 当内容格式发生变化时,您不必担心阅读长度。
  • 不需要将单个字符用作“终止符”,因为长度是预先确定的。

【讨论】:

  • 这是个好主意,但需要对我的代码和输入文件进行一些重大更改。现在,我会坚持@Paul Ogilvie 给我的建议。不过,我也想在不久的将来尝试您的建议。
  • 很抱歉这么晚才回复。我尝试了您的解决方案,效果很好,我只需要重写“write_to_binary”函数和“read_from_binary”函数。我应该发布我的解决方案代码以帮助可能遇到相同问题的其他人吗?
  • @I.Silviu 当然。在 Stack Overflow 上,我们始终鼓励您发布任何您认为可能对更多读者有所帮助的内容。
  • 好的,我已经添加了。
【解决方案2】:

您需要知道文件的格式。我假设如下:

  • 文件以一个包含文件中学生人数的四字节整数开头。

  • 每个学生的数据按名字、姓氏、年级的顺序排列。

  • 名字和姓氏的字符串以空字节结束。

  • 浮点数是 4 字节浮点数。

  • 跟随 4 字节浮点数跟随下一个学生(没有记录分界)。

现在您可以读取数据了。唯一的事情是如何确定字符串的长度,因为您必须为它们分配内存。有两种方法:

  • 使用合理大小的缓冲区(比如buf[256])并逐个字符读取,直到看到空字符(您可能希望跳过会导致缓冲区溢出的字符,因此文件格式的另一部分规范是字符串不能超过 255)。读取并使用 \0 终止缓冲区后,调用strlen,然后使用malloc 分配字符串,或者

  • 记住当前位置,逐个字符读取,计数直到看到\0,分配内存,向后查找并读取字符串。

【讨论】:

  • 关于第三点,我的结构化文件记录应该是这样的吗?Terresa\0 Minaya\0 9.50
  • 您的文件应如下所示:指定且您可以读入的任何格式。如果我假设的格式不合适,请调整我的假设并相应地调整阅读方法。
猜你喜欢
  • 2019-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 1970-01-01
  • 2020-05-06
  • 1970-01-01
  • 2012-04-21
相关资源
最近更新 更多