C语言 | 文件操作详解与实战示例

🧩 C语言 | 文件操作详解与实战示例

✍️ 作者 :凡间的八戒

🗓️ 时间 :2025-11-12

🏷️ 标签:C语言、文件操作、IO流、fopen、fread、feof、ferror


🧠 前言

在 C 语言学习中,"文件操作"是非常关键的部分。

它赋予程序数据持久化的能力,让我们从屏幕输入输出走向真实的数据存储。

本文将带你全面掌握文件操作的核心内容,包含:

  • 文件概念与类型
  • 打开与关闭
  • 文本与二进制存储方式
  • 顺序/随机读写
  • 缓冲区机制
  • feof 与 ferror 的区别(重点)

一、为什么使用文件?

内存中的数据是临时的 ,程序退出后会被清除。

若想让数据长期保存,就必须将其写入磁盘文件

📘 例如:

  • 游戏存档
  • 程序日志
  • 用户配置

这些都是通过文件实现数据持久化的典型场景。


二、什么是文件?

在程序设计中,文件分为两类:

类型 说明
程序文件 存放源代码(.c)、目标文件(.obj)、可执行文件(.exe)
数据文件 存放程序运行时需要读写的数据

三、文本文件与二进制文件

文件类型 存储形式 可读性 示例
文本文件 ASCII 编码 可直接阅读 .txt
二进制文件 内存原始格式 不可直接阅读 .bin

📘 示例:

c 复制代码
#include <stdio.h>

int main() {
    int a = 10000;
    FILE* pf = fopen("test.txt", "wb");
    fwrite(&a, 4, 1, pf); // 二进制写入
    fclose(pf);
    return 0;
}

四、文件的打开与关闭

打开与关闭函数

c 复制代码
FILE* fopen(const char* filename, const char* mode);
int fclose(FILE* stream);

打开模式速查表

模式 含义 文件不存在时
"r" 只读文本 出错
"w" 只写文本 新建
"a" 追加写文本 新建
"rb" 只读二进制 出错
"wb" 只写二进制 新建
"ab" 追加二进制 新建
"r+" 读写文本 出错
"w+" 读写文本(重建) 新建

📘 示例:

c 复制代码
#include <stdio.h>
int main() {
    FILE* pFile = fopen("myfile.txt", "w");
    if (pFile != NULL) {
        fputs("fopen example", pFile);
        fclose(pFile);
    }
    return 0;
}

五、文件的顺序读写

函数 功能
fgetc 读取单个字符
fputc 写入单个字符
fgets 读取一行
fputs 写入一行
fscanf 格式化读取
fprintf 格式化输出
fread 二进制读取
fwrite 二进制写入

📘 这些函数既可用于文件,也可用于标准流(stdin/stdout)。


六、文件的随机读写

fseek:移动文件指针

c 复制代码
fseek(FILE* stream, long offset, int origin);

ftell:返回当前位置

c 复制代码
long ftell(FILE* stream);

rewind:返回文件头

c 复制代码
rewind(FILE* stream);

📘 示例:

c 复制代码
#include <stdio.h>
int main() {
    FILE* pFile = fopen("example.txt", "wb");
    fputs("This is an apple.", pFile);
    fseek(pFile, 9, SEEK_SET);
    fputs(" sam", pFile);
    fclose(pFile);
    return 0;
}

七、文件读取结束的判断(重点)

在文件操作中,经常要判断文件是否读取完毕或发生错误。

这就需要使用 feof()ferror() ------ 它们看似相似,实则完全不同!


🧩 点击展开 | feof() 与 ferror() 的区别详解

🔹 feof ------ 判断是否到达文件末尾

c 复制代码
int feof(FILE *stream);
  • 上一次读操作到达文件结尾时返回非0。
  • 必须先读取再判断,否则不会触发。

📘 错误示例:

c 复制代码
while (!feof(fp)) { // ❌ 会多读一次
    fscanf(fp, "%d", &x);
    printf("%d\n", x);
}

📘 正确写法:

c 复制代码
while (fscanf(fp, "%d", &x) == 1)
    printf("%d\n", x);

if (feof(fp))
    printf("文件读取完毕\n");

🔹 ferror ------ 判断是否发生读写错误

c 复制代码
int ferror(FILE *stream);
  • 当发生 磁盘错误、权限不足或设备异常 时返回非0。

📘 示例:

c 复制代码
FILE* fp = fopen("test.txt", "r");
if (!fp) {
    perror("打开失败");
    return 1;
}

int c;
while ((c = fgetc(fp)) != EOF)
    putchar(c);

if (ferror(fp))
    puts("❌ 文件读写错误!");
else if (feof(fp))
    puts("✅ 文件正常结束。");

fclose(fp);

🔹 区别总结表

函数 判断内容 触发时机 用途 是否自动清除
feof 到达文件尾 读到EOF后 判断读取结束
ferror 发生错误 I/O异常后 判断设备问题

📘 可使用:

c 复制代码
clearerr(FILE* stream);

清除错误与EOF状态。


🔹 对比示例

c 复制代码
#include <stdio.h>
int main(void) {
    FILE* fp = fopen("demo.txt", "r");
    if (!fp) {
        perror("文件打开失败");
        return 1;
    }

    int c;
    while ((c = fgetc(fp)) != EOF)
        putchar(c);

    if (ferror(fp))
        printf("读取错误!\n");
    else if (feof(fp))
        printf("文件读取完毕。\n");

    fclose(fp);
    return 0;
}

🧠 总结一句话:

feof() 判断文件是否读完;

ferror() 判断是否出错;

二者互不替代,千万别混淆!


八、文件缓冲区机制

C语言使用缓冲文件系统 来提升读写性能。

写入时数据先进入缓冲区,待缓冲满或 fflush() / fclose() 调用后才真正写入磁盘。

📘 示例:

c 复制代码
#include <stdio.h>
#include <windows.h>

int main() {
    FILE* pf = fopen("test.txt", "w");
    fputs("abcdef", pf);
    printf("睡眠10秒,此时文件内容未写入。\n");
    Sleep(10000);
    fflush(pf);
    printf("刷新缓冲区后写入成功。\n");
    fclose(pf);
    return 0;
}

九、总结与学习建议

核心要点:

  • 文件操作基本步骤:fopen → 读写 → fclose
  • 文本与二进制的本质区别在于存储形式
  • 判断结束不要直接用 feof()
  • 养成使用 fflush()fclose() 的好习惯

💡 建议练习:

  1. 设计一个"学生成绩保存系统";
  2. fseek() 实现随机修改;
  3. 手动触发 I/O 错误测试 ferror()
相关推荐
数据知道8 分钟前
claw-code 源码分析:大型移植的测试哲学——如何用 unittest 门禁守住「诚实未完成」的口碑?
开发语言·python·ai·claude code·claw code
小堃学编程17 分钟前
【项目实战】基于protobuf的发布订阅式消息队列(2)—— 线程池
java·开发语言
每日任务(希望进OD版)23 分钟前
线性DP、区间DP
开发语言·数据结构·c++·算法·动态规划
怨言.24 分钟前
Java内部类详解:从基础概念到实战应用(附案例)
java·开发语言
AC赳赳老秦25 分钟前
OpenClaw image-processing技能实操:批量抠图、图片尺寸调整,适配办公需求
开发语言·前端·人工智能·python·深度学习·机器学习·openclaw
XiYang-DING26 分钟前
【Java】 Java 集合框架
java·开发语言
charlie11451419127 分钟前
嵌入式C++教程实战之Linux下的单片机编程(9):HAL时钟使能 —— 不开时钟,外设就是一坨睡死的硅
linux·开发语言·c++·单片机·嵌入式硬件·c
diving deep31 分钟前
从零构建大模型--实操--搭建python环境
开发语言·python
We་ct32 分钟前
LeetCode 172. 阶乘后的零:从暴力到最优,拆解解题核心
开发语言·前端·javascript·算法·leetcode·typescript
沉淀粉条形变量43 分钟前
rust 单例模式
开发语言·单例模式·rust