You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

未知二进制文件数据量时fread与fwrite的正确用法

问题根因
  • 统计文件内结构体数量的while循环执行完成后,文件内部读写指针已经移动到文件末尾,此时直接调用fread无法读取到任何有效数据。malloc申请到的堆内存未被写入有效值,Windows Debug模式下堆内存默认填充0xCC值,会被误识别为有效数据:0xCCCCCCCC按32位有符号int解析结果为-842150451,连续0xCC存入字符数组就会显示为乱码。
  • ReadBinaryFile函数内硬编码打开文件名为cars.dat,未使用传入的file_name形参,传入其他文件名时会打开错误目标。
  • 逐块读取计数的方式IO次数多、效率低,且很容易遗漏文件指针重置步骤,不适用于固定大小结构体存储的二进制文件读取场景。
正确实现方案

对于存储固定大小结构体的二进制文件,不需要逐块循环计数,直接通过文件长度计算元素总数即可,步骤如下:

  • 以二进制读模式打开目标文件
  • 调用fseek将文件指针移动到文件末尾,通过ftell获取文件总字节数
  • 校验总字节数是否为单个结构体大小的整数倍,避免读取损坏的格式错误文件
  • 总字节数除以单个结构体占用内存大小,即可得到文件内存储的结构体总数量
  • 调用rewind将文件指针重置到文件起始位置
  • 申请对应大小的内存空间,一次性读取所有结构体数据
  • 关闭文件句柄,返回读取到的数组指针和元素数量

修复后的完整核心代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Cars {
    int years;
    char company[16];
} Tcars;

void WriteBinaryFile(char* file_name, Tcars* p_1)
{
    FILE* f = fopen(file_name, "wb");
    // 实际使用请补充f==NULL的空指针校验
    fwrite(p_1, sizeof(Tcars), 3, f);
    fclose(f);
}

Tcars* ReadBinaryFile(char* file_name, int* pc)
{
    FILE* f = fopen(file_name, "rb");
    *pc = 0;
    // 实际使用请补充f==NULL的空指针校验
    // 移动指针到文件末尾获取总文件长度
    fseek(f, 0, SEEK_END);
    long file_total_size = ftell(f);
    if (file_total_size < 0 || file_total_size % sizeof(Tcars) != 0) {
        // 文件损坏或格式不匹配的错误处理
        fclose(f);
        return NULL;
    }
    // 计算文件内存储的结构体总数
    *pc = file_total_size / sizeof(Tcars);
    if (*pc == 0) {
        fclose(f);
        return NULL;
    }
    // 将文件指针重置到文件起始位置
    rewind(f);
    // 申请对应大小内存一次性读取全部数据
    Tcars* p_2 = malloc(sizeof(Tcars) * (*pc));
    // 实际使用请补充malloc返回NULL的空指针校验
    size_t read_count = fread(p_2, sizeof(Tcars), *pc, f);
    if (read_count != *pc) {
        // 读取失败的错误处理
        free(p_2);
        fclose(f);
        *pc = 0;
        return NULL;
    }
    fclose(f);
    return p_2;
}

int main()
{
    Tcars* ParkingLot1 = malloc(sizeof(Tcars) * 3);
    for(int i=0;i<3;i++)
    {
        ParkingLot1[i].years = 2000 + i;
    }
    strcpy(ParkingLot1[0].company, "Fiat");
    strcpy(ParkingLot1[1].company, "Ford");
    strcpy(ParkingLot1[2].company,"Toyota");
    Tcars* ParkingLot2 = NULL;
    int cars_amount = 0;
    WriteBinaryFile("cars.dat", ParkingLot1);
    ParkingLot2 = ReadBinaryFile("cars.dat", &cars_amount);
    // 此处可添加打印逻辑校验读取结果
    // for(int i=0;i<cars_amount;i++){
    //     printf("%d %s\n", ParkingLot2[i].years, ParkingLot2[i].company);
    // }
    free(ParkingLot1);
    free(ParkingLot2);
    return 0;
}
注意事项
  • 只要在Windows Debug模式下看到值为-842150451的int类型变量、或字符数组里出现连续0xCC对应的乱码,就可以直接判定对应内存未被正确写入有效数据,优先检查IO指针位置、内存拷贝逻辑是否正确。
  • 二进制文件读写必须严格匹配打开模式:写入用"wb"、读取用"rb",Windows平台下文本模式会自动转义换行符,会导致文件长度计算错误、内容读取异常。
  • 如果一定要使用逐块读取计数的逻辑,计数循环结束后必须调用rewind(f)将文件指针重置到起始位置,才能执行后续的批量读取操作,但这种写法IO效率低,考试场景下优先选择文件长度计算的方式,代码更短、出错概率更低。

内容的提问来源于stack exchange,提问作者Foch29

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 16:36:39