文件的顺序读写

官方信息技术老师·19 页·深入(追求细节与边界)·0 次浏览·2 天前
文件I/Ostdio.h缓冲机制文件指针

文件的顺序读写

看完你能区分文本/二进制读写、搞懂缓冲机制与 EOF 的判定细节

按 空格/→ 演示下一步

1 / 19 页

全部页面点击任意一页,跳回舞台从这页播放

文件I/Ostdio.h缓冲机制文件指针

文件的顺序读写

看完你能区分文本/二进制读写、搞懂缓冲机制与 EOF 的判定细节

1第 1 页 · 文件的顺序读写

什么是文件?

上页我们按顺序把字节从硬盘流出来——但这些字节到底存在哪里?为什么写完关掉程序,下次再打开还在?答案藏在一层叫「文件系统」的中间层里。

文件的物理形态
磁盘上一组占据若干扇区、带有名字和属性的字节序列
文件系统的职责
维护「文件名→扇区位置」的映射表,记录大小、权限等元数据
文件名≠文件本身
名字只是给人看的索引,真正的字节躺在磁盘的扇区里
图书馆索书号对应 →文件系统索引

索书号对应文件名,索引卡对应映射表,书架位置对应扇区

2第 2 页 · 什么是文件?

文本文件 vs 二进制文件

上一节我们认识了文件就是磁盘上的一串字节。但在读写文件时你会发现:同样是字节,有的打开能直接读懂,有的却是满屏乱码——这背后是两种不同的存储方式。

文本文件
字节按字符编码(如 ASCII、UTF-8)组织,逐字节可还原为字符
二进制文件
字节按数据结构原样写入,与字符编码无关,解读依赖程序格式
核心差异
是否多一层编码翻译:文本肉眼可读,二进制必须由程序按结构解析
典型场景
文本适合配置/源码/日志;二进制适合图像/音视频/可执行文件
明文信与摩斯密码对应 →文本与二进制文件

明文人人能直读;摩斯只是一串点划,要靠密码本才能还原内容

3第 3 页 · 文本文件 vs 二进制文件

FILE 指针与文件缓冲区

用 fopen 打开文件时拿到一个 FILE*,它不是文件本身,而是 C 标准库的「调度中心」——背后挂着一块缓冲区,决定数据何时真正落盘。

FILE* 的本质
指向 FILE 结构体的指针,结构体由 stdio 内部维护
FILE 结构体内容
含文件描述符、缓冲区地址、读写位置、错误与 EOF 标志
缓冲区的意义
磁盘 I/O 远慢于内存,先攒一批再读写,减少系统调用
何时真正落盘
缓冲区写满、调用 fflush、fclose 正常返回时刷入磁盘
三种缓冲模式
全缓冲(文件)、行缓冲(终端)、无缓冲(stderr)
快递驿站对应 →FILE 与缓冲区

FILE* 是取件码,缓冲区是驿站货架,磁盘是远端仓库

4第 4 页 · FILE 指针与文件缓冲区

文件操作的三步走

文件操作按打开、读写、关闭三步走,缺一步都可能留隐患。

1
fopen 打开
指定路径与模式(r/w/a/r+/w+/a+/b),拿到 FILE* 算建立好程序与文件的通道
2
读写数据
文本走 fprintf/fgets/fputs;二进制走 fread/fwrite;数据经缓冲区再落盘
3
fclose 关闭
刷新缓冲区把残留数据写回磁盘,释放 FILE*,否则数据可能丢失
5第 5 页 · 文件操作的三步走

文件操作的代码模板

c

标准 C 文件读写完整模板:打开→读写→关闭,每步都带回值检查。

代码高亮加载中…

L6 fopen 拿 FILE*;L7 NULL 必查;L14 fwrite 是 (buf, 大小, 个数, fp);L15 返回值看是否部分写入;L20 fclose 隐式 flush 缓冲区。

6第 6 页 · 文件操作的代码模板

fgetc 与 fputc 函数

上一页搭好了「打开-读写-关闭」的代码骨架,现在往读写这一步里填具体动作。最简单的读写单位是一个字符,对应的接口就是 fgetc 和 fputc。这俩函数虽然功能简单,但返回值藏着坑。

fgetc 读字符
从 FILE 指针当前位置读一字符,读完光标自动后移一格
fputc 写字符
把一个字符写到文件指定处,写完光标同样自动后移
必须用 int 接收
fgetc 返回 int 而非 char,否则无法区分合法字符与 EOF
EOF 末尾标记
读到文件尾或出错时返回 EOF,其值为 -1
流水线逐件分拣对应 →fgetc 逐字符读取

每过一个产品返回一件,分拣完末尾返回空箱 EOF

7第 7 页 · fgetc 与 fputc 函数

字符读写代码示例

c

用 fgetc 和 fputc 把一个文件逐字符复制到另一个文件——把刚学的 API 串成完整程序。

代码高亮加载中…

高亮行串成字符复制的完整链路:int 装返回值、NULL 守护打开、EOF 守护循环、fputc 配对 fgetc、fclose 收尾。

8第 8 页 · 字符读写代码示例

文件字符 I/O vs 标准 I/O

两个看似无关的函数,其实是一对「通用版」与「特供版」——搞清楚参数,就一次都忘不了。

fgetc / fputc
  • 操作任意已打开的 FILE* 文件流
  • 必须显式传入 FILE* 参数
  • 通用:磁盘文件、管道、网络都支持
getchar / putchar
  • 仅操作 stdin / stdout 两个标准流
  • 无参数,固定绑定标准流
  • 专用:只服务键盘输入与屏幕输出
getchar() 就是 fgetc(stdin) 的语法糖;操作普通文件用 fgetc/fputc,操作标准流用 getchar/putchar。
9第 9 页 · 文件字符 I/O vs 标准 I/O

fgets 与 fputs 函数

字符级 fgetc/fputc 是一字一字搬砖,要读一整行得自己写循环。字符串级接口一步到位,但行为细节藏了不少坑。

两个函数的签名
fgets(buf,n,fp) 读至多 n-1 字符;fputs(s,fp) 写到 \0 为止
fgets 三种停止
读够 n-1 个、遇 \n、遇 EOF——任一即停
换行的不同处理
fgets 把 \n 一并存进缓冲区;fputs 不会自动追加 \n
返回值怎么判
fgets 返 NULL=到末尾或出错;fputs 返 EOF=写失败
读一句话对应 →fgets 读一行

n 是耐心上限;遇句号(\n)或翻到末页(EOF)即停;句号留在笔记里

10第 10 页 · fgets 与 fputs 函数

字符串读写代码示例

c

用 fgets 按行读、fputs 按行写,把整份文本文件打印到屏幕。

代码高亮加载中…

fgets 一次读一行(含换行符),读到末尾返回 NULL 退出循环;缓冲区大小写 sizeof(line) 而非硬编码 256,改数组长度时不用动循环。

11第 11 页 · 字符串读写代码示例

fgets 与 gets 的安全区别

两者名字只差一个字母,安全级别却天差地别——gets 没有长度参数,是缓冲区溢出的经典源头。

fgets(安全)
  • 调用时必须传入缓冲区大小 n
  • 保留换行符并自动加 '\0'
  • C 标准一直保留,主流推荐
gets(危险)
  • 不接长度参数,能读多少读多少
  • 丢弃换行符,只加 '\0'
  • C11 已从标准库移除
字符串读取一律用 fgets:gets 不限长度必然溢出,C11 已将其从标准库移除。
12第 12 页 · fgets 与 gets 的安全区别

fprintf 与 fscanf 函数

前面 fputs 只能写整串、fgets 只能读整行。要存「姓名:张三,年龄:20」这种带格式内容或按格式解析,该怎么办?这就要靠 fprintf 和 fscanf。

fprintf 接口
fprintf(文件指针, 格式串, ...),按格式把数据写入指定文件
fscanf 接口
fscanf(文件指针, 格式串, ...),按格式从文件读取数据
对比 printf
格式串语法与 printf 相同,仅多首个 FILE* 参数指定目标
返回值
fprintf 返回写入字符数;fscanf 返回成功匹配项数,可判成败
结构体存取
字段用 fprintf 写一行,读时 fscanf 一行还原成结构体
同一份格式模板对应 →printf 与 fprintf

模板相同,printf 写到屏幕,fprintf 写到指定文件

fprintf(FILE* fp, "格式串", ...)fscanf(FILE* fp, "格式串", ...)\text{fprintf}(\text{FILE* }fp,\ \text{"格式串"},\ ...) \quad \text{fscanf}(\text{FILE* }fp,\ \text{"格式串"},\ ...)
13第 13 页 · fprintf 与 fscanf 函数

格式化读写代码示例

c

用 fprintf 把一个结构体数组写入文本文件,再按相同格式用 fscanf 读回并打印。

代码高亮加载中…

写端 fprintf 用 "%s %d %f" 把三个字段拼成一行;读端 fscanf 必须用完全相同的格式串,数组字段直接传名(已是地址)、标量字段必须 &,返回值 == 字段数才算成功。

14第 14 页 · 格式化读写代码示例

fread 与 fwrite 函数

前面 fgetc、fgets、fprintf 都在处理文本字符。但如果要读写一个完整的结构体、一段二进制数据,就需要按"整块"搬运的接口——fread 与 fwrite。

函数签名
ptr 缓冲区、size 单元素字节数、nmemb 元素个数、stream 文件
返回值
成功读写的元素个数(不是字节数),可用于判断是否完整
适用场景
读写结构体、二进制数据块,比文本方式更快且无损
核心区别
不做格式转换,原样搬字节;不会因换行或精度丢失
边界情况
返回值小于请求数可能表示到末尾或出错,需 feof/ferror 区分
整页复印对应 →fread/fwrite

fgetc 像逐字抄写,fread 像整页复印——原样复制,无格式转换

15第 15 页 · fread 与 fwrite 函数

数据块读写代码示例

c

用 fwrite / fread 把整个结构体当作字节块原样读写

代码高亮加载中…

wb/rb 强制二进制模式;fwrite 把 sizeof(Student) 个字节直接落盘,fread 再按同样大小读回,字段自动复原。

16第 16 页 · 数据块读写代码示例

文本模式 vs 二进制模式

同样是打开文件,加不加「b」差别很大——文本模式会偷偷改你的换行符,二进制模式一字节都不动。

文本模式 "r"
  • Windows 下 \n 自动转 \r\n,读时再转回
  • 配 fprintf/fscanf/fgets/fputs 等文本函数
  • 适合日志、配置、源码等人类可读内容
二进制 "rb"
  • 字节原样存取,不做任何转换
  • 配 fread/fwrite 按数据块读写
  • 适合图片、结构体等需保留原始字节的数据
读写可读文本用文本模式;存结构体或非文本数据必须用二进制模式——混用 Windows 下会因换行转换导致数据错位。
17第 17 页 · 文本模式 vs 二进制模式

文件读写函数全景图

  • 按数据形态选函数,分四档粒度
  • 结构体与原始字节走二进制通道
  • 缓冲区放大批量 I/O 的性能差距
  • 读循环靠 feof/ferror,不用 EOF
延伸主题:随机读写:fseek/ftell错误处理:ferror/clearerr标准 I/O 与系统调用
18第 18 页 · 文件读写函数全景图

课后思考

深入探究文件操作的边界与陷阱

课后思考
深入探究文件操作的边界与陷阱
19第 19 页 · 课后思考
文件的顺序读写 · 知识图解