C语言中带字节指示器的|分隔结构体字段存读跨平台问题求助
Windows下正常但Linux/在线IDE中无法正确读取带长度标识的文本数据
我需要将结构体的三个字段以|分隔保存到文本文件,同时在每个字段串前写入一个字节标识该串的长度。编写的写入和读取程序在Windows上运行正常,但在Linux和部分在线IDE中无法正确读取长度标识字节,求解决方法。
写入程序
#include <stdio.h> #include <string.h> struct compras { char nome[50]; int qtd; float valor; }; int main() { struct compras dados[7] = {{ "manteiga", 1, 4.5 }, { "pao", 1, 7.99 }, { "arroz", 2, 10.95 }, { "feijao", 3, 9.4 }, { "carne", 2, 21.90 }, { "detergente", 3, 1.65 }, { "sabao", 2, 3.45 }}; int k; char str[100]; FILE *f = fopen("compras.txt", "w"); int tam; for (k = 0; k < 7; k++) { sprintf(str, "%s|%d|%.2f", dados[k].nome, dados[k].qtd, dados[k].valor); tam = strlen(str); printf("%c%s", tam, str); char str1[100]; sprintf(str1, "%c%s", tam, str); fputs(str1, f); } fclose(f); return 0; }
读取程序
#include <stdio.h> int main() { FILE *f = fopen("compras.txt", "r"); int ch; char str[100]; while (1) { ch = fgetc(f); if (ch == EOF) break; fgets(str, ch + 1, f); printf("%s\n", str); } fclose(f); return 0; }
问题原因
- 文本模式与二进制模式的差异:
Windows的文本模式会对换行符(\n)做自动转换(存储为\r\n),而Linux使用原生的\n。但更关键的是,你用文本模式写入的长度字节(可能是不可打印的ASCII控制字符),在不同系统的文本模式下可能被篡改或解析异常。 sprintf处理二进制字节的风险:sprintf(str1, "%c%s", tam, str)会把tam当作ASCII字符处理,如果tam对应的是像\0、\r这类控制字符,sprintf的行为不可控,甚至可能截断字符串,导致写入的长度标识和实际内容不匹配。fgets的行为限制:fgets会在读取到换行符或达到指定长度时停止,如果你存储的字符串中意外包含换行符,或者文本模式下的换行转换导致长度计算偏差,都会引发读取错误。
解决方案
1. 改用二进制模式读写文件
二进制模式("wb"/"rb")会原样读写所有字节,避免系统对字符的自动转换,这是跨平台兼容的核心。
2. 直接写入长度字节,避免sprintf
用fputc直接写入长度值,再用fputs或fwrite写入字符串,跳过不可靠的sprintf格式化。
3. 读取时用fread替代fgets
fread可以精确读取指定长度的字节,不受换行符干扰。
修正后的写入程序
#include <stdio.h> #include <string.h> struct compras { char nome[50]; int qtd; float valor; }; int main() { struct compras dados[7] = {{ "manteiga", 1, 4.5 }, { "pao", 1, 7.99 }, { "arroz", 2, 10.95 }, { "feijao", 3, 9.4 }, { "carne", 2, 21.90 }, { "detergente", 3, 1.65 }, { "sabao", 2, 3.45 }}; int k; char str[100]; FILE *f = fopen("compras.txt", "wb"); // 二进制写入模式 int tam; for (k = 0; k < 7; k++) { sprintf(str, "%s|%d|%.2f", dados[k].nome, dados[k].qtd, dados[k].valor); tam = strlen(str); // 先写长度字节,再写字符串 fputc(tam, f); fputs(str, f); // 控制台输出可以保留原来的方式,不影响文件写入 printf("%c%s", tam, str); } fclose(f); return 0; }
修正后的读取程序
#include <stdio.h> #include <string.h> int main() { FILE *f = fopen("compras.txt", "rb"); // 二进制读取模式 int len; char str[100]; while ((len = fgetc(f)) != EOF) { // 读取指定长度的字符,注意要留位置存终止符 if (fread(str, 1, len, f) == len) { str[len] = '\0'; // 手动添加字符串终止符 printf("%s\n", str); } } fclose(f); return 0; }
内容的提问来源于stack exchange,提问作者André Backes
相关产品推荐
相关产品推荐

