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

无法用fscanf读取二进制文件:C语言CSV与BIN互转反序列化问题

问题根源与解决方案

你的序列化代码从根上就错了,这才导致反序列化完全无法工作。

核心错误:序列化时存的是指针而非实际数据

你定义的struct order里全是char*指针,调用strtok分割CSV行后,这些指针实际指向栈上buffer数组的片段。每次循环fgets会覆盖buffer的内容,而你用fwrite(&order_buffer, sizeof(order_buffer), 1, output_file)写入的是这些指针的内存地址,不是字符串的实际内容。等反序列化时,读出来的都是无效的旧地址,自然无法获取数据。

另外,你原反序列化代码里的order_buffer指针都是未初始化的NULL,直接用fscanf往NULL指针写入会触发未定义行为(大概率崩溃)。

修正方案:用固定长度数组存储字段内容

最简单可靠的方式是把结构体的char*改成固定长度的char数组,确保能容纳CSV字段的最大长度,序列化时复制实际字符串内容,而非指针。

第一步:重新定义结构体

#define MAX_FIELD_LEN 64  // 根据你的CSV实际字段长度调整
#define MAX_LINE_LENGTH 1024

struct order {
    char row_id[MAX_FIELD_LEN];
    char order_id[MAX_FIELD_LEN];
    char order_date[MAX_FIELD_LEN];
    char customer_id[MAX_FIELD_LEN];
    char city[MAX_FIELD_LEN];
    char state[MAX_FIELD_LEN];
    char postal_code[MAX_FIELD_LEN];
    char region[MAX_FIELD_LEN];
    char product_id[MAX_FIELD_LEN];
    char category[MAX_FIELD_LEN];
    char sub_category[MAX_FIELD_LEN];
    char price[MAX_FIELD_LEN];
};

第二步:修正序列化函数

用strncpy把strtok分割出的字符串内容复制到结构体数组中,再写入二进制文件:

void serialize(FILE* input_file, FILE* output_file) {
    char buffer[MAX_LINE_LENGTH];
    struct order order_buffer = {0};

    // 跳过CSV表头行
    fgets(buffer, MAX_LINE_LENGTH, input_file);
    
    while (fgets(buffer, MAX_LINE_LENGTH, input_file) != NULL) {
        // 去除换行符,避免被strtok当成字段内容
        buffer[strcspn(buffer, "\n")] = '\0';

        char* token = strtok(buffer, ",");
        if (token) strncpy(order_buffer.row_id, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.order_id, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.order_date, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.customer_id, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.city, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.state, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.postal_code, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.region, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.product_id, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.category, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.sub_category, token, MAX_FIELD_LEN - 1);

        token = strtok(NULL, ",");
        if (token) strncpy(order_buffer.price, token, MAX_FIELD_LEN - 1);

        // 写入整个结构体的实际数据
        fwrite(&order_buffer, sizeof(struct order), 1, output_file);
        // 重置结构体,避免下一次循环残留旧数据
        memset(&order_buffer, 0, sizeof(struct order));
    }
}

第三步:编写正确的反序列化函数

直接用fread读取二进制文件中的结构体,再按CSV格式拼接字段写入文件:

void deserialize(FILE* input_file, FILE* output_file) {
    struct order order_buffer = {0};

    // 写入CSV表头
    fprintf(output_file, "row_id,order_id,order_date,customer_id,city,state,postal_code,region,product_id,category,sub_category,price\n");

    // 循环读取每个结构体数据
    while (fread(&order_buffer, sizeof(struct order), 1, input_file) == 1) {
        // 按CSV格式输出字段
        fprintf(output_file, "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
                order_buffer.row_id,
                order_buffer.order_id,
                order_buffer.order_date,
                order_buffer.customer_id,
                order_buffer.city,
                order_buffer.state,
                order_buffer.postal_code,
                order_buffer.region,
                order_buffer.product_id,
                order_buffer.category,
                order_buffer.sub_category,
                order_buffer.price);
        // 重置结构体
        memset(&order_buffer, 0, sizeof(struct order));
    }
}

可选进阶方案:动态长度序列化

如果CSV字段长度波动很大,不想浪费内存,可以改用“长度+内容”的方式序列化:

  1. 序列化时,对每个字符串先写入一个int类型的长度值,再写入字符串内容。
  2. 反序列化时,先读取长度,用malloc分配对应内存,再读取字符串内容。

这种方式需要手动管理内存,记得用完后free避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:43:10