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

C语言注释fprintf写入逻辑后fscanf无法读取r+模式打开文件的问题

问题根因分析
  • UTF-8 BOM头干扰
    程序首次运行自行写入文件时,不会在文件开头写入UTF-8 BOM标识(3个特殊字节:0xEF、0xBB、0xBF),写入完成调用rewind后直接从流缓冲区读取内容,无BOM干扰因此读取正常。如果后续使用Windows记事本等工具打开文件并保存,工具会默认给UTF-8文件添加BOM头,再次用程序打开读取时,BOM头会被当作普通内容读入姓名字段,导致后续内容和格式串完全错位,性别、年龄、身高、体重字段匹配全部失败;如果存储这些字段的变量未初始化,就会表现为空值或0。
  • fscanf格式串逻辑错误
    %c格式符不会自动跳过空格、换行、制表符等前置空白字符。如果格式串中%c前没有手动添加空格,读完姓名字段后,文件指针会停在姓名后的第一个空白字符位置,这个空白符会被直接读入性别变量,后续的%d、%f因为遇到非数字字符匹配失败,直接终止读取,后续字段无法被赋值。
  • 其他易忽略的违规操作
    • 调用fopen打开r+模式文件时未检查返回值,文件不存在、路径权限不足导致打开失败,后续读操作全部无效
    • 注释写入逻辑时未清理干净残留的写操作,写操作完成后未调用rewind、fseek或fflush就直接执行读操作(C标准明确规定:使用带+的更新模式打开文件时,读写状态切换必须执行流定位或缓冲区刷新操作,否则行为未定义),文件指针不在文件开头,读取位置错误
    • 未检查fscanf的返回值,格式匹配失败时未做异常处理,直接输出未初始化的变量值,表现为空或0
修复后完整可运行代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define NAME_LEN 32
#define FILE_PATH "filetest4.txt"

// 定义结构化数据类型存储人员信息
typedef struct {
    char name[NAME_LEN];
    char gender;
    int age;
    float height;
    float weight;
} Person;

int main() {
    // 以r+模式打开已存在文件,强制做打开结果检查
    FILE* fp = fopen(FILE_PATH, "r+");
    if (fp == NULL) {
        perror("文件打开失败");
        return EXIT_FAILURE;
    }

    // 检测并跳过UTF-8 BOM头
    char bom_check[3] = {0};
    if (fread(bom_check, 1, 3, fp) == 3) {
        // 不是BOM头则回退指针到文件开头
        if (!(bom_check[0] == (char)0xEF && bom_check[1] == (char)0xBB && bom_check[2] == (char)0xBF)) {
            fseek(fp, 0, SEEK_SET);
        }
        // 是BOM头则直接从偏移3字节的位置开始读取即可
    } else {
        // 文件长度不足3字节,回退到开头
        fseek(fp, 0, SEEK_SET);
    }

    Person p;
    // 初始化结构体,避免匹配失败时输出随机垃圾值
    memset(&p, 0, sizeof(p));

    // 注意:%c前必须加空格,强制跳过所有前置空白符(空格、换行、制表符)
    int read_ret = fscanf(fp, "%s %c %d %f %f", p.name, &p.gender, &p.age, &p.height, &p.weight);
    if (read_ret != 5) {
        printf("读取失败,仅成功匹配%d个字段,请检查文件格式\n", read_ret);
        fclose(fp);
        return EXIT_FAILURE;
    }

    // 输出读取结果
    printf("读取成功:\n");
    printf("姓名:%s\n", p.name);
    printf("性别:%c\n", p.gender);
    printf("年龄:%d\n", p.age);
    printf("身高:%.1f cm\n", p.height);
    printf("体重:%.1f kg\n", p.weight);

    /************************************
    若后续需要添加写入逻辑,必须遵守规则:
    读切换到写之前,必须调用fflush(fp)或fseek/rewind定位流
    写切换到读之前,必须调用fseek/rewind定位流,否则会触发未定义行为
    ************************************/

    fclose(fp);
    return EXIT_SUCCESS;
}
关键知识点说明
  • 尽量不要用系统自带记事本编辑程序读写的文本文件,避免被自动添加BOM头、修改换行符格式,推荐使用VS Code、Notepad++等代码编辑器,保存时选择UTF-8无BOM格式。
  • 使用%c、%[]这类不会自动跳过空白符的fscanf格式占位符时,必须根据业务需求判断是否需要在格式串前加空格跳过前置空白。
  • 所有文件操作必须做错误检查:fopen判断返回值是否为NULL,fscanf/fread等读操作检查返回值是否符合预期的读取数量,禁止直接操作未赋值的变量。
  • 使用带+的更新模式打开文件时,严格遵守读写切换规则,避免未定义行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:42:29