【C语言进阶】给数据一个“家”:从零开始掌握文件操作

目录

[1. 文本文件 vs 二进制文件:存 10000 的两种姿势](#1. 文本文件 vs 二进制文件:存 10000 的两种姿势)

[2. 文件的"生老病死":打开与关闭](#2. 文件的“生老病死”:打开与关闭)

[2.1 核心指针:FILE*](#2.1 核心指针:FILE*)

[2.2 fopen 与 fclose](#2.2 fopen 与 fclose)

[3. 读写函数的"全家桶"](#3. 读写函数的“全家桶”)

[💡 重点推荐:fprintf / fscanf](#💡 重点推荐:fprintf / fscanf)

[💡 序列化神器:sprintf / sscanf](#💡 序列化神器:sprintf / sscanf)

[4. 文件的"随机穿越":fseek 与 ftell](#4. 文件的“随机穿越”:fseek 与 ftell)

[5. 经典面试坑:feof 的误用](#5. 经典面试坑:feof 的误用)

[6. 为什么需要缓冲区?](#6. 为什么需要缓冲区?)

[📝 总结](#📝 总结)


前言:

写了这么久代码,不知道你有没有和我一样的苦恼:

辛辛苦苦在控制台输入了一堆学生成绩,程序一关,数据全没了。下次运行还得重新输一遍。

以前我们的数据都在内存 里,内存是"健忘"的,断电即失。 今天我们要学习文件操作 ,把数据存到硬盘 里,实现数据的持久化


1. 文本文件 vs 二进制文件:存 10000 的两种姿势

文件在硬盘上都是二进制存的,但根据组织形式,我们把文件分为文本文件二进制文件

举个经典的栗子:整数 10000

  • 文本形式: 把它看作字符 '1','0','0','0','0'。每个字符占 1 字节,共占用 5 个字节

  • 二进制形式: 直接存它的二进制补码。在 32 位系统下,int4 个字节 (10 27 00 00,小端存储)。

结论: 二进制通常更省空间,但文本文件肉眼可读 。


2. 文件的"生老病死":打开与关闭

操作文件就三步走:打开 -> 读写 -> 关闭

2.1 核心指针:FILE*

每个被使用的文件在内存中都有一个"文件信息区",用来存放文件名、状态、位置等。这个区域由系统声明为 FILE 结构体。我们通过 FILE* 指针来维护它 。

2.2 fopen 与 fclose

C

复制代码
// 打开文件
FILE* fopen(const char* filename, const char* mode);
// 关闭文件
int fclose(FILE* stream);

⚠️ 避坑指南:

  1. 路径: 可以是绝对路径,也可以是相对路径 。

  2. 判空: 打开失败会返回 NULL一定要检查返回值!

  3. 模式: w 会清空文件(慎用!),a 是追加,r 是只读。如果是二进制文件,记得加上 b(如 wb, rb) 。


3. 读写函数的"全家桶"

C 语言提供了一堆读写函数,我把它们整理成了表格,方便记忆:

功能 函数名 (读/写) 适用对象 备注
单个字符 fgetc / fputc 所有流 就像 getchar/putchar 的文件版
文本行 fgets / fputs 所有流 读文本最常用,读到 \n 或满 buffer 为止
格式化 fscanf / fprintf 所有流 就像 scanf/printf,能处理结构体
二进制块 fread / fwrite 文件流 直接读写内存块,效率高,虽然肉眼看不懂

💡 重点推荐:fprintf / fscanf

这两个函数简直是神技。如果你有一个结构体:

C

复制代码
struct Stu { char name[20]; int age; float score; };

fprintf(fp, "%s %d %f", s.name, s.age, s.score) 就能直接把结构体格式化写进文件,非常方便 。

💡 序列化神器:sprintf / sscanf

这两个家伙不操作文件,它们操作字符串

  • sprintf:把结构体变成字符串(序列化)。

  • sscanf:把字符串还原成结构体(反序列化)。 在做网络传输或日志记录时,这俩函数非常有用 。


4. 文件的"随机穿越":fseek 与 ftell

谁说读文件只能从头读到尾?我们可以像进度条一样随意拖动。

  • fseek: 定位文件指针。

    C

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

    origin 有三种:SEEK_SET (开头), SEEK_CUR (当前), SEEK_END (末尾) 。

  • ftell: 告诉我当前指针在哪(偏移量) 。

  • rewind: 一键回到开头 。


5. 经典面试坑:feof 的误用

很多同学(包括以前的我)喜欢这样写循环:

C

复制代码
// ❌ 错误写法
while (!feof(fp)) {
    fscanf(fp, ...);
}

千万别这么写!

feof 的作用是:当文件读取结束了,用来判断是因为"读到了末尾"结束的,还是因为"读取出错"结束的 。 它不应该用来作为循环停止的条件。正确的判断应该基于 fgetcfscanf 的返回值。


6. 为什么需要缓冲区?

你有没有想过,为什么我们用 fwrite 写数据,不是写一个字节硬盘就动一下?

那样硬盘早就挂了。

C 语言采用缓冲文件系统

  1. 输出缓冲区: 内存里的数据先堆到缓冲区,装满 了或者遇到 fflush / fclose 时,才一起送到硬盘 。

  2. 输入缓冲区: 从硬盘一次读一堆数据到缓冲区,程序再从缓冲区里拿。

实战教训: 如果你写了数据但还没 fclose,程序突然崩了,数据可能会丢失!因为它们还卡在缓冲区里没送出去。 所以,文件操作完一定要记得 fclose,它不仅关闭文件,还会自动刷新缓冲区 。


📝 总结

文件操作是 C 语言与外部世界沟通的桥梁。

  1. 选对模式: 文本用文本流,图片/视频用二进制流 (rb, wb)。

  2. 选对函数: 存配置信息用 fprintf,存大量数据用 fwrite

  3. 善后工作: 永远记得 fclose,防止内存泄漏和数据丢失。

相关推荐
进击的小头2 小时前
行为型模式:策略模式的C语言实战指南
c语言·开发语言·策略模式
ʚB҉L҉A҉C҉K҉.҉基҉德҉^҉大3 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
爱编码的小八嘎3 小时前
C语言对话-5.通过任何其他名字
c语言
哈__3 小时前
多模融合 一体替代:金仓数据库 KingbaseES 重构企业级统一数据基座
数据库·重构
老邓计算机毕设3 小时前
SSM医院病人信息管理系统e7f6b(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·医院信息化·ssm 框架·病人信息管理
步步为营DotNet3 小时前
深度剖析.NET中IHostedService:后台服务管理的关键组件
服务器·网络·.net
2601_949613023 小时前
flutter_for_openharmony家庭药箱管理app实战+药品分类实现
大数据·数据库·flutter
Ares-Wang4 小时前
网络》》路由引入 、路由控制 》》路由策略 route-policy 、Filter-Policy(过滤策略)
网络·智能路由器
dyyx1114 小时前
使用Scikit-learn进行机器学习模型评估
jvm·数据库·python
踢足球09295 小时前
寒假打卡:2026-01-27
数据库