一、文件操作,到底在操作什么?
各位同学,咱们今天聊点实在的。C语言的文件操作,说白了就是让程序和磁盘上的数据“对话”。你写了个程序,算了一堆结果,总不能每次关掉就丢了吧?那就得存起来。怎么存?靠文件。
我刚开始学的时候,总觉得文件操作很神秘。不就是读写个文件吗?后来踩了坑才明白,这里头门道不少。今天咱们就把文件的概念、类型、缓冲区、还有那个无处不在的FILE*指针,一次性说透。
核心一句话:文件是数据的容器,程序通过文件指针与操作系统协作,完成数据的持久化。
1.1 文件的概念——程序之外的“记忆”
文件是什么?说白了,就是磁盘上的一段数据,有个名字,有起始和结束。程序运行时,数据在内存里;程序退出,内存释放,数据就没了。文件就是用来解决这个问题的——把数据搬到磁盘上,下次还能用。
我记得第一次做嵌入式项目,要把传感器采集的温度数据存下来。当时想:直接写个数组不就行了?结果一断电,数据全没了。嗯,从那以后,我再也不敢小看文件操作了。
在C语言里,文件被抽象成一个字节流。你不需要关心它到底存在磁盘的哪个扇区,只需要通过标准库函数去读写就行。操作系统帮你搞定底层。
1.2 文本文件 vs 二进制文件——两种“语言”
文件类型,其实就两种:文本文件和二进制文件。区别在哪?我直接说人话:
- 文本文件:存的是字符,比如字母、数字、标点。你用记事本打开能看懂。每个字符对应一个ASCII码,读写时会有自动的换行符转换(比如Windows下'\n'会转成'\r\n')。
- 二进制文件:存的是数据的原始字节。比如一个int整数,直接存4个字节。你用记事本打开是乱码,但程序读起来快,没有格式转换。
你想想看,如果你要存一个结构体,里面既有整数又有浮点数,用文本文件存,你得先转成字符串,再写进去,读的时候再解析回来,麻烦不麻烦?二进制文件直接fwrite整个结构体,一锤子买卖。
我的建议:如果数据需要跨平台、跨语言交换,用文本文件(比如JSON、XML)。如果追求性能和存储效率,尤其是嵌入式系统里,用二进制文件。我在做单片机数据采集时,都是直接写二进制,省空间、速度快。
| 对比项 | 文本文件 | 二进制文件 |
|---|---|---|
| 存储内容 | 字符序列(ASCII/Unicode) | 数据原样字节 |
| 可读性 | 人类可读 | 机器可读,人看不懂 |
| 换行符处理 | 自动转换(\n ↔ \r\n) | 不转换,原样读写 |
| 读写效率 | 较低(需格式化转换) | 高(直接内存拷贝) |
| 典型用途 | 配置文件、日志、源码 | 可执行文件、图片、数据库 |
1.3 文件缓冲区——看不见的“快递中转站”
为什么要有缓冲区?直接读写磁盘不行吗?行,但慢。磁盘的读写速度比内存慢好几个数量级。如果每次读写一个字节都去操作磁盘,程序性能会惨不忍睹。
所以C语言的标准库在内存里给你划了一块区域,叫文件缓冲区。数据先攒到缓冲区里,满了或者遇到特定条件(比如换行、关闭文件),再一次性刷到磁盘。反过来读也一样,一次读一大块到缓冲区,程序再从缓冲区取。
我曾经在一个数据采集项目里,没注意缓冲区的问题,写数据时程序突然掉电,结果缓冲区里的数据全丢了。从那以后,我养成了一个习惯:关键数据写完后,手动调用fflush()强制刷新缓冲区。
注意:缓冲区不是无限大的。默认大小通常是512字节或4KB(取决于系统)。如果你写的数据很重要,记得在适当位置调用fflush()或fclose(),确保数据真正落盘。
1.4 文件指针(FILE*)——操作文件的“遥控器”
在C语言里,你没法直接操作文件。你得通过一个中介——FILE结构体指针。这个结构体里包含了文件的所有信息:文件位置、缓冲区状态、读写模式、错误标志等等。
你每次调用fopen(),系统就给你分配一个FILE结构体,返回它的指针。之后的所有操作——读、写、定位、关闭——都通过这个指针来完成。说白了,它就是你和文件之间的遥控器。
#include <stdio.h>
int main() {
// 打开文件,返回文件指针
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return -1;
}
// 通过fp读写文件...
char buffer[100];
fgets(buffer, sizeof(buffer), fp);
printf("读到内容: %s", buffer);
// 关闭文件,释放资源
fclose(fp);
return 0;
}
注意看:fopen()的第一个参数是文件名,第二个参数是模式字符串。模式有很多种:"r"(读)、"w"(写,清空原内容)、"a"(追加)、"rb"(二进制读)等等。选错模式,轻则读不到数据,重则把原有文件覆盖了。
避坑指南:我曾经在升级固件时,用"w"模式打开了一个配置文件,结果文件内容被清空了。后来我改成"r+"模式(读写,不截断),才避免了这个悲剧。所以,打开文件前,一定想清楚你要干什么。
1.5 知识体系总览
下面这张图,把本章的核心概念串起来了。你看一遍,心里就有谱了。
1.6 小结——你该记住什么
好了,咱们捋一遍今天的内容:
- 文件是程序与磁盘之间的数据桥梁,本质是字节流。
- 文本文件存字符,人类可读,但有换行符转换开销。二进制文件存原始数据,效率高,但不可读。
- 缓冲区是性能的关键,但也要注意数据丢失的风险。重要数据记得
fflush()。 - FILE*是操作文件的唯一入口,打开模式选错,后果很严重。
我个人习惯是:只要涉及文件操作,第一件事就是检查fopen()返回值是否为NULL。别嫌麻烦,这个习惯救过我很多次。下一章咱们会深入讲文件读写函数,到时候你会觉得今天的基础打得特别值。
课后小练习:写一个程序,分别用文本模式和二进制模式写入一个整数100,然后用十六进制查看工具(比如xxd)看看两个文件的内容有什么不同。你会发现,文本模式存的是"100"三个字符的ASCII码,二进制模式存的是0x64一个字节。
公众号:蓝海资料掘金营,微信deep3321