无法用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字段长度波动很大,不想浪费内存,可以改用“长度+内容”的方式序列化:
- 序列化时,对每个字符串先写入一个
int类型的长度值,再写入字符串内容。 - 反序列化时,先读取长度,用
malloc分配对应内存,再读取字符串内容。
这种方式需要手动管理内存,记得用完后free避免内存泄漏。
内容的提问来源于stack exchange,提问作者DarkRemino
相关产品推荐
相关产品推荐

