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

C语言!feof读取txt文件崩溃问题排查与正确读取实现方法

代码故障根因排查

你的程序崩溃、输出乱码、编译报错是多个问题叠加导致的,!feof的错误使用是原因之一,但不是全部:

  • 结构体成员名大小写不匹配:定义的成员是cityID,fscanf里写的是&newCity.CityID,编译器找不到对应成员才会弹出“是否需要使用->”的误导性报错。
  • fscanf格式串完全不匹配文件格式:你的文件用||做分隔符,格式串却写了%[^#]匹配#作为终止符,根本读不到有效内容。另外字符数组名本身就是首地址,前面不需要加&,强行传地址属于未定义行为。
  • 文件打开判断逻辑混乱:判断条件写得冗余矛盾,报错时传入的是FILE*类型的文件指针而不是文件名字符串,所以会打印内存地址类乱码;函数定义为void返回值却写了return 1,会直接触发编译错误。
  • 无数组越界保护:cityArr最多存20条数据,循环没有长度判断,文件行数超限时会直接写穿内存导致崩溃。
  • 资源泄漏:打开的fprice文件句柄全程没有关闭。

为什么不推荐用!feof做循环条件

feof()的本质是检查上一次IO操作是否因为触碰文件末尾而失败,不是提前预判下一次读取是否会到文件尾。
用while(!feof(fp))做循环的固定缺陷是:最后一行数据读取完成后,EOF标志还未被置位,循环会多执行一次。这次读取操作会直接失败,写入未初始化的垃圾数据到结构体,这也是绝大多数人读文件时最后一行多出乱码的核心原因。


不依赖!feof的正确实现

直接把fscanf的返回值作为循环判断依据即可:fscanf成功匹配到2个目标字段时会返回2,读到文件尾、匹配失败时会返回EOF或小于2的值,从根源上避免多跑一次循环的问题。
修正后的可运行代码如下:

#include <stdio.h>
#include <conio.h>

typedef struct City {
    char cityName[20];
    char cityID[10];
} City;

int readFiles() {
    const char *txtMap = "map.txt";
    const char *txtPrice = "deliver_price.txt";
    FILE *fmap = fopen(txtMap, "r");
    FILE *fprice = fopen(txtPrice, "r");
    City cityArr[20];
    int j, a = 0;

    // 修正文件打开判断逻辑
    if (fmap == NULL || fprice == NULL) {
        if (fmap == NULL) {
            printf("\n\n\n\t\t\t\t\tError: Couldn't open file %s\n\n\n\n\n\n\n", txtMap);
        } else {
            printf("\n\n\n\t\t\t\t\tError: Couldn't open file %s\n\n\n\n\n\n\n", txtPrice);
        }
        printf("\n\n\n\t\t\t\t\tPress enter to continue\n\t\t\t\t\t");
        // 提前关闭已打开的文件句柄,避免资源泄漏
        if (fmap) fclose(fmap);
        if (fprice) fclose(fprice);
        return 1;
    }

    City newCity;
    // 加数组长度判断防越界,直接用fscanf返回值控制循环
    // 格式串加长度限制防止缓冲区溢出,开头空格自动跳过换行/空白字符
    while (a < 20 && fscanf(fmap, " %19[^|]||%9[^\n]", newCity.cityName, newCity.cityID) == 2) {
        cityArr[a] = newCity;
        a++;
    }

    printf("reading file successful\n");
    fclose(fmap);
    fclose(fprice);

    for (j = 0; j < a; j++) {
        printf("\n%s || %s\n", cityArr[j].cityName, cityArr[j].cityID);
    }
    getch();
    return 0;
}

实现注意点:

  • %19[^|]限制最多读19个字符到城市名字段,留1字节存字符串结束符\0,避免长地名写爆数组;同理%9[^\n]给城市ID字段预留结束符位置。
  • 所有文件句柄在函数返回前统一关闭,避免资源泄漏。
  • 循环条件加a < 20判断,从根源上杜绝数组越界崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 03:01:25