程序里的数据,如果只放在内存里,一旦程序退出、内存回收,就再也找不回来了。想让这些「回忆」被永久保存,就要把它们写进文件。

程序文件与数据文件

从功能上看,文件分两种:

程序文件:源程序(.c)、目标文件(.obj)、可执行程序(.exe)。
数据文件:程序运行时真正读写的数据,比如运行的输出或要读取的内容。

一个文件名由路径 + 主干 + 后缀组成,比如 C:\code\test.txt。

文本文件 vs 二进制文件

按数据的组织形式,又分这两类:如果按 ASCII 形式存在外存就是文本文件(记事本打开能看懂),如果直接按二进制存储、不做转换就是二进制文件(记事本打开看不懂)。

// 整数 10000
// ASCII 存储:'1''0''0''0''0' → 占 5 字节
// 二进制存储:00 00 27 10     → 占 4 字节(小端:10 27 00 00)

流与 FILE 指针

为了屏蔽各种外部设备的差异,C 语言抽象出了「流」的概念。程序启动时默认打开三个标准流:stdin、stdout、stderr,类型都是 FILE*。

FILE 指针示意图:pf 指针指向文件信息区再连接到文件
每个 FILE* 指针关联一个「文件信息区」,再连到真正的文件

打开文件时系统会自动创建一个 FILE 结构并填充信息,我们只需一个 FILE* 来维护它。

打开与关闭

FILE* fp = fopen("data.txt", "w");   // "w" 只写;还有 r / a / + 等模式
if (fp == NULL) { perror("fopen"); return 1; }
// ...读写操作...
fclose(fp);   // 关闭时也会刷新缓冲区
fp = NULL;

顺序读写的一串函数

函数用途
fgetc / fputc读写单个字符
fgets / fputs读写一行文本
fscanf / fprintf格式化输入 / 输出
fread / fwrite二进制读写数据块
sscanf / sprintf从字符串提取 / 写成字符串

还有一对判断小帮手:feof(是否到文件结尾)和 ferror(是否读取出错)。

随机读写:指针想去哪儿就去哪儿

fseek(fp, 6, SEEK_SET);   // 从文件开头偏移 6
// origin:SEEK_SET 开头 / SEEK_CUR 当前位置 / SEEK_END 末尾
ftell(fp);                // 查当前偏移量
rewind(fp);               // 回到开头(= fseek(fp, 0, SEEK_SET))

文件缓冲区:写进去不等于落盘

数据不是每次都立刻写进磁盘,而是先进入内存里的文件缓冲区,装满(或换行、或程序结束)才一起送出去。所以写完记得 fclose,中途要确保落盘可以用 fflush(fp) 强制刷新。

fputs("abcdef", fp);   // 还没有真正写进磁盘
fflush(fp);            // 强制刷新 → 数据落盘
文件操作的可爱插画
把数据写进磁盘,就像把回忆小心地收藏起来

更新文件:r+ / w+ / a+

  • r+:保留内容,可定位覆盖,文件不存在则打开失败。
  • w+:清空内容、从头写,文件不存在自动新建。
  • a+:保留内容,默认在文件末尾追加,适合记日志。

一个小提醒:读写切换前,记得 fflush 或 fseek / rewind 重新定位,避免读到「旧位置」。

✿ 摘要

  • 文件让数据永久保存,分程序文件 / 数据文件、文本 / 二进制文件。
  • 通过流操作文件,FILE* 维护文件信息区。
  • fopen 打开、fclose 关闭;fseek/ftell/rewind 随机定位。
  • 数据先进入缓冲区,用 fflush / fclose 确保落盘。
  • 更新模式 r+ / w+ / a+ 分别对应保留覆盖、清空重写、末尾追加。

愿当下的花如你所见般美丽