一、文件使用前置
1.1 为什么使用文件?
如果没有了文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失,等再次运行程序,是看不到上次程序的数据的,所以我们使用文件将数据进行持久化的保存。
1.2 什么是文件
磁盘(硬盘)上的文件就是文件
在程序设计中,文件一般分为两种:程序文件 、数据文件。
1.2.1 程序文件
程序文件包括源程序文件(.c),目标文件(.obj),可执行文件(.exe)
简而言之程序文件就是存放程序源代码、编译后的目标代码或可执行指令,能被编译器 / 操作系统加载执行的文件
1.2.2 数据文件
文件的内容不一定是程序,也可以是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或着输出内容的文件
存放程序运行所需的原始数据或运行产生的结果数据,供程序通过 IO 函数读写的文件
1.2.3 文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用
文件名包含3个部分:文件路径+文件名主干+文件后缀
文件标识常被称为文件名
1.3 二进制文件和文本文件
根据数据的组织形式,数据文件被分为文本文件或者二进制文件
数据在内存中以二进制的形式存储,如果不加以转换的输出到外存的文件中,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储的文件就是文本文件。
一个数据在文件中存储:
字符一律以ASCII形式存储,数值型既可以用ASCII形式存储,也可以使用二进制形式存储

二、文件的打开和关闭
2.1 流和标准流
2.1.1 流
程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河
c程序针对文件、画面、键盘等的数据输入输出的操作都是通过流的
一般情况下,我们想向流里写数据或者从流里读取数据,都是要打开流,然后操作
2.1.2 标准流
为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
因为C语言程序在启动的时候就默认打开了3个流:
①:stdin(标准输入流),在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据
②:stdout(标准输出流),大多数的环境中输出至显示器,printf函数就是将信息输出到标准输出流中
③:stderr(标准错误流),大多数环境中输出显示到显示器界面
2.2 文件指针
缓冲文件系统中,关键的概念是"文件类型指针 ",简称"文件指针"
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存方文件的相关信息(文件的名字、文件状态及文件当前的位置等),这些信息是保存在一个结构体变量中,该结构体类型是系统声明的,取名FILE

我们定义pf指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区,通过该文件信息区中的信息就能够访问该文件,也就是说,通过文件指针变量能够间接找到与它相关的文件。

2.3 文件的打开和关闭
文件在读写之前应该先打开文件,在使用结束后应该关闭文件
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系
ANSIC 规定使用fopen函数来打开函数,fclose来关闭文件

mode表示文件打开的模式,下面都是文件的打开模式:

三、文件的顺序读写
3.1 顺序读写函数介绍

①: int fgetc(FILE* stream)

从stream指向的文件读取一个字符,成功返回该字符的ASCII值,读取失败或到文件末尾时返回EOF
②:int fputc (int ch,FILE* stream)

ch:要写入的字符(实际传ASCII值)
将ch对应的字符写入stream指向的文件,成功返回写入的字符,失败返回EOF
③:char *fgets(char* str,int num,FILE* stream)

str:存储读取字符串的字符数组
num:最多读取num-1个字符
从stream指向的文件中读取一行字符串(包含换行符),存入str,读取失败或到文件末尾返回NULL
④:int fputs(const char* str,FILE* stream)

str:要写入的字符串
将str指向的字符串写入stream指向的文件,不自动添加换行符,成功返回非负值,失败返回EOF
⑤: int fscanf(FILE* stream,const char * format,...)

format:格式控制字符串(如%d)
...:接收数据的变量地址列表(&a)
⑥ int fprintf(FILE* stream,const char* format,...)

将数据按format格式写入stream指向的文件,返回成功写入的字符数
⑦:size_t fread(void* ptr,size_t size,size_t count,FILE* stream)

ptr:存储读取数据的内存地址(如数组/结构体指针)
size:单个数据块的字节大小
count:要读取的数据块数量
从stream指向的文件中读取count字节的二进制数据,存入ptr,返回实际成功读取的数据块数量
⑧:size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)

将ptr指向的count字节二进制数据写入stream指向的文件,返回实际成功写入的数据块数量
四、文件的随机读写
4.1 fseek
根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)

其中origin有三个位置可以选择(分别是文件头和文件尾还有当前位置)
4.2 ftell
返回文件指针相对于起始位置的偏移量

4.3 rewind
让文件指针的位置回到文件的起始位置

五、文件读取结束的判定
feof
在文件读取过程中,不能用feof函数的返回值直接来判断文件是否结束
feof的作用是当文件读取结束时,判断读取结束的原因是否是遇到文件尾
文本文件读取是否结束,判断返回值是否为EOF(fgetc)或者NULL(fgets)
二进制文件的读取结束判断返回值是否小于实际要读的个数。
六、文件缓冲区
ANSIC 标准采用"缓冲文件系统" 处理的数据文件的,所谓缓冲**文件系统是指系统自动地在内存中为 程序中每一个正在使用的文件开辟一块"文件缓冲区"。**从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输 入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓 冲区的大小根据C编译系统决定的
因为有缓冲区的存在,C语言在操作文件时,需要做刷新缓冲区或者在文件操作结束的时候关闭文件,如果不做可能导致读写文件的问题