三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

2026/08/08 C语言的文件标准io

2026/08/08 C语言的文件标准io

标准C库中关于文件输入输出的函数接口一般被称为标准IO,访问文件常用的标准IO函数有fopen()、fread()、fwrite()、fclose()、fgetc()、fputc()、fgets()、fputs()、fprintf()、fscanf()等。

打开文件
想要对文件进行读写访问的前提是必须先打开文件,标准IO中提供了一个函数叫做fopen(),用户只需要包含标准输入输出头文件 #include <stdio.h> 即可调用。
fopen函数是有返回值的,如果文件打开成功,则返回值返回指向该文件的文件流指针,如果文件打开失败,则返回值为NULL
image
FILE类型其实是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,比如包括用于实际I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。头文件stdio.h中有关于FILE类型的相关描述,
image

打开头文件<libio.h>之后,可以找到关于FILE结构体类型的定义,可以发现FILE结构体类型中的成员数量很多
FILE结构体类型中有一个成员是FILE类型的指针变量chain,该指针可以指向下一个被打开文件的文件信息区,也就是可以把FILE类型当做数据结构中的链表的结点,结点中除了可以存储数据域之外,还可以利用指针域存储下一个结点的地址。

简单理解:用户可以在一个程序中利用fopen函数打开多个文件,每次打开一个文件,内核就会从堆内存中申请一块FILE结构体大小的空间用来存储文件的所有信息,然后按照文件打开的顺序把每个打开的文件的结构体形成一条链表,然后使用链表头进行管理。

注意:打开文件的目的无非就是对文件进行读写操作,所以每次当程序运行的时候已经有三个文件流被打开,分别是标准输入stdin、标准输出stdout、标准出错stderr,这三者在stdio.h中也是FILE指针。
image
所以内核在管理被打开文件的时候,链表中已经有三个结点存在,然后再把新节点头插入到链表中。
image
注意:使用标准IO的时候,是不可以反复关闭相同的文件,因为释放已经被释放的堆内存,会导致段错误!!

读取数据
用户打开文件后可以从文件中读取数据,标准C库中提供了多个读取函数来满足用户的不同需求,这些函数大体分为三类:字符读取(fgetc)、按行读取(fgets)、按块读取(fread)。

(1)字符读取
标准库中提供了一个fgetc函数,通过C99标准可以知道该函数的作用是从文件指针stream指向的文件中读取一个字符,并在读取一个字节后把文件的光标位置向后移一个字节,然后把读取到的字符所对应的ASCII码通过返回值返回。

在调用该函数时如果文件的光标已经到达文件末尾或者遇到读取错误时,则函数会返回EOF,EOF是文件结束标志,其实是个宏定义,宏定义的值为 -1,在头文件libio.h中有相关描述。
image

另外,在标准库中还提供了另一个函数getc(),这个函数的作用等效于fgetc()函数,只不过getc()函数的实现是利用宏定义而已。
image
image

(2)按行读取
标准库中提供了一个fgets函数,通过C99标准可以知道该函数的作用是从文件指针stream指向的文件中读取一行字符,并把读取的字符存储在指针s所指向的字符串内,当读取到n-1个字符、或者已经读取到文件末尾(EOF)、或者读取到换行符’\n’时,则函数调用停止。
image
用户调用fopen打开文件之后,可以把数据写入到文件中以及从文件中读取数据,但是实现读取和写入的过程中其实内核并没有直接操作文件,而是在操作指向文件的结构体指针FILE,也就是用户写入的数据和读取的数据会先存储在FILE结构体的缓冲区中,当用户调用刷新缓冲区的函数或者其他读写函数时,FILE结构体的缓冲区会被刷新,数据才会被系统写入文件。
image
可以看到,每当使用标准IO的读操作函数,试图将数据从文件 a.txt读取出来时,数据都会流过标准输入缓冲区,然后再在适当的时刻冲洗(或称刷新,flush)到内核缓冲区,最后才真正得到数据。

缓冲区的出现其实就是由于输入设备和输出设备对于数据的读写速度比较慢,其实就是CPU为了降低输入输出次数,目的是为了提高运行效率,避免长时间的等待,所以内核就在内存中提供了一块空间作为缓冲区,缓冲区也可以称为缓存(Cache),是属于内存空间的一部分。
对于标准输出stdout而言默认是采用行缓冲的,而对于标准出错stderr而言默认是采用无缓冲的,对于普通文件而言默认是采用全缓冲的。

全缓冲:指的是当缓冲区被填满就立即把数据冲刷到文件、或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件,一般读写文件的时候会采用

无缓冲:指的是没有缓冲区,直接输出,一般linux系统的标准出错stderr就是采用无缓冲,这样可以把错误信息直接输出。

行缓冲:指的是当缓冲区被填满(一般缓冲区为4KB,就是4096字节)或者缓冲区中遇到换行符’\n’时,或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件中,一般操作IO设备时会采用,比如printf函数就是采用行缓冲。

当然,全缓冲和行缓冲除了以上几种情况外,当程序结束时缓冲区也会被刷新,另外,也可以采用函数库中的fflush函数手动刷新缓冲区。

(3)按块读取

标准库中提供了一个fread函数,通过C99标准可以知道该函数的作用是从给定的文件输入流stream中读取最多nmemb个对象到指针ptr指向的字符串中,每个对象的大小为size字节,函数返回成功读取的对象个数,若出现错误或到达文件末尾,则可能小于nmemb。若size或nmemb为零,则fread函数返回0且不进行其他动作。

image
可以通过标准库中提供的两个函数区分是出现读取错误还是到达文件末尾,一个函数是feof(),另一个则是ferror函数。
image

写入文件
(1)字符写入
image
image

(2)按行写入
image
image

(3)按块写入
image
image

关闭文件
利用fopen()打开文件之后内核会申请一块堆内存用来存储文件信息,申请的堆内存大小就是FILE结构体类型的大小,那么如果用户完成了对文件的读写访问之后,则需要利用fclose()函数来关闭文件,这样这块堆内存就会被内核先从链表中删除,然后再释放掉。
对fclose函数的成功调用会导致刷新流指向的流,并关闭关联的文件。流的任何未写入的缓冲数据都将被传送到主机环境以写入文件;任何未读的缓冲数据都将被丢弃。无论调用是否成功,流都与文件解除关联,setbuf或setvbuf函数设置的任何缓冲区都与流解除关联(如果自动分配,则释放)。
image

文件位置指示器
每个被打开文件的结构体中都有一个位置指示器(简单理解:位置指示器是文件光标),注意:被打开的文件的光标默认是在文件开头的。
(1)设置位移
image
image

(2)获取位移

image
image

格式访问

标准库中除了以上关于文件读写的函数之外,还提供了一些可以对文件进行格式化读写的函数接口,一般常用的关于文件IO的格式化函数有printf、fprintf、scanf、fscanf、sprintf、snprintf。
image
image

← 返回列表