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

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;
}

问题原因

  1. 文本模式与二进制模式的差异:
    Windows的文本模式会对换行符(\n)做自动转换(存储为\r\n),而Linux使用原生的\n。但更关键的是,你用文本模式写入的长度字节(可能是不可打印的ASCII控制字符),在不同系统的文本模式下可能被篡改或解析异常。
  2. sprintf处理二进制字节的风险:
    sprintf(str1, "%c%s", tam, str)会把tam当作ASCII字符处理,如果tam对应的是像\0、\r这类控制字符,sprintf的行为不可控,甚至可能截断字符串,导致写入的长度标识和实际内容不匹配。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:43:24