C语言 1000问 · 文件I/O Q3:怎么正确判断文件读完?为什么常说 while (!feof(fp)) 是错的?

来源:Flomo 原始笔记|查看原笔记|迁移日期:2026-10-31

C语言 1000问 · 文件I/O Q3:怎么正确判断文件读完?为什么常说 while (!feof(fp)) 是错的?

▎ 五大学习法:费曼学习法——把「feof 的坑」讲透

feof 到底是什么

  • feof(fp) 不是「预测下一次读会不会到末尾」,它报告的是「上一次读是否因为碰到文件末尾而结束」。
  • 它是「事后标记」:只有真正读到 EOF(且读到失败)之后,它才变真。
  • 同理 ferror 报告上次读是否出错,也属事后。

while (!feof(fp)) 为什么错

  • 错误写法:while (!feof(fp)) { fread(buf, …); process(buf); }
  • 问题:读完最后一块时 feof 还是 false(这次读成功、没触发 EOF 标记),循环又进去一次;这一次 fread 读到 0 字节才置 EOF 标记。于是 process 拿着「上次的旧数据」多处理一遍。
  • 更糟的是如果没检查 fread 返回值,就用了无效 / 陈旧数据。

正确写法

  • 用读操作的返回值控制循环:
    • while ((n = fread(buf, 1, sizeof buf, fp)) > 0) { process(buf, n); }
    • 或 fgets:while (fgets(line, sizeof line, fp) != NULL) { … }
    • 或 getc:int c; while ((c = getc(fp)) != EOF) { … }
  • 判断「是否真出错」用 ferror,判断「正常读完」用 feof。
  • 标准套路:先读,读到的元素数 < 期望时,再看 feof / ferror 区分「正常结束」还是「出错」。

几个相关坑

  • getc 返回 int 不是 char(要用 EOF 区分),写成 char c = getc(…) 会把 0xFF 误判成 EOF。
  • fgets 不保证读满一行:行太长会截断,要检查是否含 ‘\n’。
  • 读一条记录(fread 结构体)要检查返回是否为 1(成功读一条),部分读取要处理。

一句话:feof 是「上次读是否因到末尾而结束」的事后标记,不能用来预测下一次;正确做法是「用读函数的返回值控制循环」,读完再用 feof / ferror 区分正常结束还是出错。

📎 问题库(专栏⑭导航)→ https://v.flomoapp.com/mine/?memo_id=MjQ1NzM0NTk5

📎 文件I/O Q2(缓冲)→ https://v.flomoapp.com/mine/?memo_id=MjU2NDUwNDQ5

C语言