1、为什么使用文件
2、什么是文件
3、二进制文件和文本文件
4、文件的打开和关闭
5、文件的顺序
6、文件的随机读写
7、文件读取结束的判定
8、文件缓冲区
int main()
{
int a = 0;
scanf("%d", &a); // 这个就是将输入的数字放入文件中
printf("%d\n", a);
return 0;
}
一、问什么使用文件
- 内存数据断电丢失,文件可永久保存数据;
- 方便数据重复读取、批量处理;
- 实现多程序数据共享。
二、什么是文件
磁盘(硬盘)上的文件是文件,但是在程序设计中,我们一般谈到的文件有两种:程序文件、数据文件
程序文件:
源程序文件(.c)
-
后缀:
.c目标文件(
.obj/.o) -
后缀:Windows 下
.obj,Linux 下.o可执行文件(
.exe) -
后缀:Windows 系统下
.exe
数据文件
专门用来存放程序运行所需数据的文件,不包含代码指令。作用:给程序提供输入数据、保存程序运行结果,实现数据长久存储。
本章讨论的是数据文件
在以前各章所处理数据的输入和输出都是以终端为对象的,即从终端的键盘输入数据并行结果显示在显示器上,其实有的时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读到内存中,使用这里处理的就是磁盘上的文件。
文件名:
一个文件要有一个唯一的文件标识,以便用户识别和引用。文件名包含三个部分,**文件路径 + 文件名主干 + 文件后缀(扩展名);例如:**E:\project\main.c为了方便起见,文件标识常被称为文件名。
二进制文件和文本文件
文本文件(ASCII 文件)
1. 存储规则
把每一个字符 按它对应的 ASCII 码 存储,内容就是我们能看懂的文字、数字、符号。
2. 特点
- 可用记事本、写字板直接打开阅读、编辑;
- 以
\n、EOF(-1)作为结束标志; - 占用空间略大,读写速度稍慢;
- 适合存放文字、普通数据。
二进制文件
1. 存储规则
数据直接按照内存中的二进制原样保存,不转换成 ASCII 字符。
2. 特点
- 记事本打开显示乱码,只能由对应程序读取;
- 存储紧凑、占用空间小、读写速度快;
- 原样保存变量、结构体、数组,适合存大批量数据、自定义类型;
- 没有专门的换行分隔符。

流和标准流
流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入、输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念。我们可以把流想象成流淌着的字符的河流。c 程序针对文件、画面、键盘等的数据输入,输出的操作都是通过流操作的,一般情况下我们想要向流里写数据,或者从流中读取数据,都要打开流,然后操作。
标准流
那为什么我们从键盘输入数据,向屏幕上输出数据并没有打开流?那是因为 c 语言程序在启动的时候默认打开了三个流:
stdin标准输入流 → 绑定键盘,scanf函数就是从标准输入流中读取数据的stdout标准输出流 → 绑定显示器,printf函数就是将信息输出到标准输出流中stderr标准错误流 → 绑定显示器,大多数环境中输出到显示器界面
这默认打开了这三个流,我们使用 scanf、printf 的函数就可以直接进行输入,输出操作的。stdin、stdout、stder三个流是类型是:FILE*,通常称为文件指针。 C 语言通过 FILE 文件指针 管理流。
文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。在编写程序的时候,在打开文件的同时都会返回一个 FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
fclose
语法:int fclose ( FILE * stream );
- 头文件:
#include <stdio.h> - 形参:文件指针 (必须是已经通过
fopen成功打开的指针) - 返回值:
- 关闭成功 :返回 0
- 关闭失败 :返回 非 0 值
功能 关闭形参 stream 所关联的文件,解除文件指针和文件之间的关联。
- 解除内部缓冲区与流的绑定;
- 刷新输出缓冲区 :缓冲区里还没写到磁盘的数据,全部写入文件(防止数据丢失);
- 清空输入缓冲区:缓冲区中还未读取的数据,直接丢弃。
- 参数 :
FILE *类型的流 / 文件指针。 - 返回值 :成功返回
0,失败返回非 0 值。
文件的顺序读写:
表格
| 函数名 | 函数原型 | 核心功能 | 关键参数 | 返回值 | 高频考点 |
|---|---|---|---|---|---|
fopen |
FILE *fopen (char *filename, char *mode); |
打开文件,建立文件指针与文件的关联 | filename:文件路径 / 名 mode:打开方式 |
成功:返回文件FILE*指针 失败:返回NULL |
必须判断打开是否成功;路径转义符\\;打开方式的选用 |
fclose |
int fclose (FILE *stream); |
关闭文件,解除指针与文件的关联,刷新缓冲区 | stream:已打开的文件指针 |
成功:返回0 失败:返回非 0 值 |
必须配合fopen使用;防止数据丢失;释放系统资源 |
fprintf |
int fprintf (FILE*stream, char *format, 输出列表); |
文本文件顺序写入,按格式将内存数据写入文件 | stream:文件指针format:格式控制串 |
成功:返回写入的字符数 失败:返回负数 | 文本文件写专用;用法和printf几乎一致,仅多文件指针参数 |
fscanf |
int fscanf (FILE*stream, char *format, 地址列表); |
文本文件顺序读取,从文件读取数据存入内存变量 | stream:文件指针format:格式控制串 |
成功:返回成功读取的变量数失败 / 到文件尾:返回EOF |
文本文件读专用;变量必须加&;和scanf用法一致 |
fwrite |
int fwrite (void *ptr, int size, int count, FILE *stream); |
二进制文件顺序写入,按字节块写入数据 | ptr:数据首地址size:单个数据字节数count:数据个数 |
成功:返回实际写入的完整数据块数 | 二进制文件写专用;适合数组 / 结构体 / 大批量数据 |
fread |
int fread (void *ptr, int size, int count, FILE *stream); |
二进制文件顺序读取,按字节块读取数据 | ptr:接收数据的内存地址其余参数同fwrite |
成功:返回实际读取的完整数据块数 | 二进制文件读专用;和fwrite配对使用 |
表格
| 流名称 | 系统预设指针 | 对应设备 | 核心作用 | 常用函数 |
|---|---|---|---|---|
| 标准输入流 | stdin |
键盘 | 从键盘读取数据,输入到程序中 | scanf、getchar、fscanf(stdin,...) |
| 标准输出流 | stdout |
显示器 | 把程序数据输出到屏幕上 | printf、putchar、fprintf(stdout,...) |
| 标准错误流 | stderr |
显示器 | 专门输出程序的错误、警告信息 | 报错提示专用 |
表格
| 打开方式 | 含义 | 核心规则 | 适用场景 |
|---|---|---|---|
r |
只读(文本文件) | 文件必须存在,否则打开失败;只能读,不能写 | 读取已有的文本文件数据 |
w |
只写(文本文件) | 文件不存在则新建;文件存在则清空原有全部内容,再写入 | 新建文本文件、覆盖原有文件内容 |
a |
追加(文本文件) | 文件不存在则新建;文件存在则在末尾续写内容,不覆盖原有数据 | 在已有文本文件末尾添加新内容 |
rb |
只读(二进制文件) | 同r,仅针对二进制文件 |
读取已有的二进制文件数据 |
wb |
只写(二进制文件) | 同w,仅针对二进制文件 |
新建 / 覆盖二进制文件 |
ab |
追加(二进制文件) | 同a,仅针对二进制文件 |
在二进制文件末尾续写内容 |
feof和ferror
表格
| 函数 | 原型 | 功能 | 返回值 | 使用场景 |
|---|---|---|---|---|
| feof | int feof(FILE *fp); |
判断是否到达文件末尾 | 到文件尾:返回非 0 未到尾:返回0 | 循环读文件,判断是否读完 |
| ferror | int ferror(FILE *fp); |
判断文件读写是否出错 | 出错:返回非 0 正常:返回0 | 检测读写过程 |
fputs和fgets
表格
| 函数 | 功能 | 原型 | 适用文件 | 核心特点 |
|---|---|---|---|---|
| fputs | 向文件写入字符串 | int fputs(char *str, FILE *fp); |
文本文件 | 不自动加换行、\0 不写入文件 |
| fgets | 从文件读取字符串 | char *fgets(char *str, int n, FILE *fp); |
文本文件 | 最多读 n-1 个字符;读到 \n 会一并存入;遇文件尾返回NULL |
规则总览
- 正常读到 换行符
\n:- 只要还没读满
n-1位,会把\n一并存入数组; - 然后自动追加
\0作为字符串结束。
- 只要还没读满
- 未遇到
\n,但字符数达到n-1:- 停止读取,不接收后续字符,也不收
\n; - 末尾直接补
\0。
- 停止读取,不接收后续字符,也不收
- 读到文件末尾:返回
NULL。
对比一组函数
一、输入类函数对比(scanf /fscanf/sscanf)
表格
| 函数名称 | 核心功能 | 标准函数原型 | 核心输入源 | 适用场景 | 核心特点 | 返回值规则 | 高频考点 / 易错点 |
|---|---|---|---|---|---|---|---|
| scanf | 从标准输入流读取格式化数据 | int scanf (const char *format, 地址列表); |
键盘(终端) | 日常程序的终端手动输入、控制台数据读取 | 1. 系统自动打开标准输入流,无需手动开关 2. 输入数据以空格 / 回车 / 制表符分隔 3. 是最基础、最常用的输入函数 | 成功:返回成功读取的变量个数 失败 / 输入结束:返回 EOF(-1) |
1. 读取变量必须加取地址符&(最常考易错点) 2. 格式符必须与变量类型严格匹配 3. 无法读取带空格的完整字符串 |
| fscanf | 从文件流读取格式化数据 | int fscanf (FILE *fp, const char *format, 地址列表); |
磁盘文本文件 | 从已打开的文本文件中读取数据、文件内容解析 | 1. 必须先通过fopen打开文件,获取有效文件指针fp 2. 用法与scanf几乎完全一致,仅多 1 个文件指针参数3. 按文件内容顺序读取,文件指针自动后移 |
成功:返回成功读取的变量个数 失败 / 到文件末尾:返回 EOF(-1) |
1. 必须先判断文件是否打开成功(fp == NULL) 2. 仅适用于文本文件,二进制文件不推荐使用 3. 读取结束后必须用fclose关闭文件 |
| sscanf | 从内存字符串中读取格式化数据 | int sscanf (const char *str, const char *format, 地址列表); |
内存中的字符数组 / 字符串常量 | 字符串解析、从已有字符串中提取指定格式数据、字符串拆分 | 1. 输入源是内存里的字符串,不涉及键盘 / 文件操作 2. 不会修改原字符串,仅从其中读取数据3. 可实现复杂的字符串格式提取,是字符串处理高频函数 | 成功:返回成功读取的变量个数 失败 / 字符串结束:返回 EOF(-1) |
1. 第一个参数是字符串首地址,不是文件指针 2. 原字符串必须以\0结尾,否则会读取越界 3. 常与sprintf配对使用,实现字符串的格式化读写 |
二、输出类函数对比(printf /fprintf/sprintf)
表格
| 函数名称 | 核心功能 | 标准函数原型 | 核心输出目标 | 适用场景 | 核心特点 | 返回值规则 | 高频考点 / 易错点 |
|---|---|---|---|---|---|---|---|
| printf | 向标准输出流输出格式化数据 | int printf(const char *format, 输出列表); |
显示器(终端屏幕) | 日常程序的控制台结果输出、调试信息打印 | 1. 系统自动打开标准输出流,无需手动开关 2. 是最基础、最常用的输出函数 3. 输出内容直接显示在终端屏幕上 | 成功:返回成功输出的字符总数 失败:返回负数 | 1. 格式符必须与输出变量类型严格匹配2. 输出字符串时,数组名 / 指针无需加&3. 可通过\n实现换行、\t实现制表对齐 |
| fprintf | 向文件流输出格式化数据 | int fprintf(FILE *fp, const char *format, 输出列表); |
磁盘文本文件 | 向已打开的文本文件写入数据、生成带格式的文本文件 | 1. 必须先通过fopen打开文件,获取有效文件指针fp 2. 用法与printf几乎完全一致,仅多 1 个文件指针参数 3. 按顺序写入文件,文件指针自动后移 |
成功:返回成功写入的字符总数 失败:返回负数 | 1. 必须先判断文件是否打开成功(fp == NULL) 2. 仅适用于文本文件,二进制文件不推荐使用 3. 写入结束后必须用fclose关闭文件,否则数据可能丢失 |
| sprintf | 向内存字符串输出格式化数据 | int sprintf (char *str, const char *format, 输出列表); |
内存中的字符数组 | 字符串拼接、格式化生成新字符串、数字转字符串、字符串组装 | 1. 输出目标是内存里的字符数组,不涉及屏幕 / 文件操作 2. 自动在生成的字符串末尾添加结束符\0 3. 可实现复杂的字符串格式化拼接,是字符串处理高频函数 |
成功:返回成功写入的字符总数 (不含末尾的\0)失败:返回负数 |
1. 第一个参数是目标字符数组首地址,不是文件指针 2. 目标数组必须足够大,否则会造成缓冲区溢出(核心易错点) 3. 常与sscanf配对使用,实现字符串的格式化读写 |
文件的随机读写
基本概念
- 顺序读写:从文件开头往后依次读写,文件指针只能向后移动,不能跳转。
- 随机读写 :可人为移动文件指针,直接跳到文件任意位置读写,不用从头开始读。
- 核心:靠 文件位置指针定位
- 适用:二进制文件、结构体、数组(考试重点)
注意:随机读写一般搭配 二进制文件 (
rb/wb),文本文件很少用。
表格
| 函数 | 功能 | 原型 |
|---|---|---|
fseek |
移动文件位置指针(定位) | int fseek(FILE *fp, long offset, int origin); |
ftell |
获取当前指针距离文件开头的字节数 | long ftell(FILE *fp); |
rewind |
把指针重置到文件开头 | void rewind(FILE *fp); |
1. rewind () 重置文件指针到开头
1)功能
将文件位置指针直接跳回文件起始位置。
2)语法
rewind(FILE *fp);
- 无返回值,无额外参数。
3)使用场景
读完一遍文件后,想从头再读一次 ,不用重新 fopen。
示例
rewind(fp); // 指针回到文件开头
2. ftell () 获取当前指针位置
1)功能
计算当前文件指针 距离文件首部的字节数,返回字节偏移量。
2)语法
long ftell(FILE *fp);
- 返回值:long 类型,当前偏移字节数。
3)典型用途
求文件总大小
// 1.指针移到文件末尾
fseek(fp, 0, SEEK_END);
// 2.获取总字节数
long size = ftell(fp);
printf("文件大小:%ld 字节\n", size);
3. fseek () 重点(随机读写核心)
1)函数原型
int fseek(FILE *fp, long offset, int origin);
三个参数逐个拆解(必背)
参数 1:FILE *fp
已打开的文件指针(一般二进制文件 rb/wb)
参数 2:long offset 偏移量
- 正数:向文件尾部 向后移动
- 负数:向文件头部 向前移动
- 单位:字节
参数 3:int origin 参考起点(三种固定宏,必须记)
表格
| 宏名 | 数值 | 参考位置 |
|---|---|---|
SEEK_SET |
0 | 文件开头(最常用) |
SEEK_CUR |
1 | 当前指针位置 |
SEEK_END |
2 | 文件末尾 |
2)组合写法(高频定位)
-
从文件开头向后移 10 字节
fseek(fp, 10, SEEK_SET);
-
从当前位置向后移 5 字节
fseek(fp, 5, SEEK_CUR);
-
从文件末尾向前移 8 字节(读最后一段数据)
fseek(fp, -8, SEEK_END);
-
等价于
rewind(回到开头)fseek(fp, 0, SEEK_SET);
3)返回值
- 定位成功:返回 0
- 定位失败:返回 非 0 值
文件缓冲区
