文件IO学习笔记第三章:读取数据
大家好,我是小智!
用户打开文件后可以从文件中读取数据,标准C库中提供了多个读取函数来满足用户的不同需求,这些函数大体分为三类:字符读取(fgetc)、按行读取(fgets)、按块读取(fread)。
字符读取
fgetc函数


标准库中提供了一个fgetc函数,通过C99标准可以知道该函数的作用是从文件指针stream指向的文件中读取一个字符,并在读取一个字节后把文件的光标位置向后移一个字节,然后把读取到的字符所对应的ASCII码通过返回值返回。
在调用该函数时如果文件的光标已经到达文件末尾或者遇到读取错误时,则函数会返回EOF,EOF是文件结束标志,其实是个宏定义,宏定义的值为 -1,在头文件libio.h中有相关描述。

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


getchar函数

练习
- 在本地磁盘打开一个存储少量数据的文本demo.txt,利用fgetc函数把文本中的字符输出到屏幕,当文本中所有字符都输出完成后就结束程序。

按行读取
fgets函数

标准库中提供了一个fgets函数,通过C99标准可以知道该函数的作用是从文件指针stream指向的文件中读取一行字符,并把读取的字符存储在指针s所指向的字符串内,当读取到n-1个字符、或者已经读取到文件末尾(EOF)、或者读取到换行符\n时,则函数调用停止。

思考
- 为什么fgets函数读取到换行符\n时会结束?fgets函数中的参数n的意义是什么?
回答:用户调用fopen打开文件之后,可以把数据写入到文件中以及从文件中读取数据,但是实现读取和写入的过程中其实内核并没有直接操作文件,而是在操作指向文件的结构体指针FILE,也就是用户写入的数据和读取的数据会先存储在FILE结构体的缓冲区中,当用户调用刷新缓冲区的函数或者其他读写函数时,FILE结构体的缓冲区会被刷新,数据才会被系统写入文件。

可以看到,每当使用标准IO的读操作函数,试图将数据从文件 a.txt读取出来时,数据都会流过标准输入缓冲区,然后再在适当的时刻冲洗(或称刷新,flush)到内核缓冲区,最后才真正得到数据。
缓冲区的出现其实就是由于输入设备和输出设备对于数据的读写速度比较慢,其实就是CPU为了降低输入输出次数,目的是为了提高运行效率,避免长时间的等待,所以内核就在内存中提供了一块空间作为缓冲区,缓冲区也可以称为缓存(Cache),是属于内存空间的一部分。
根据IO设备的不同,可以把缓冲区分为输入缓冲区和输出缓冲区,同样,根据刷新形式的不同,可以把缓冲区分为三种:全缓冲、行缓冲、无缓冲。
-
全缓冲:指的是当缓冲区被填满就立即把数据冲刷到文件、或者在关闭文件、读取文件内 容以及修改缓冲区类型时也会立即把数据冲刷到文件,一般读写文件的时候会采用
-
无缓冲:指的是没有缓冲区,直接输出,一般linux系统的标准出错stderr就是采用无缓冲, 这样可以把错误信息直接输出。
-
行缓冲:指的是当缓冲区被填满(一般缓冲区为4KB,就是4096字节)或者缓冲区中遇到 换行符’\n’时,或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把 数据冲刷到文件中,一般操作IO设备时会采用,比如printf函数就是采用行缓冲。
当然,全缓冲和行缓冲除了以上几种情况外,当程序结束时缓冲区也会被刷新,另外,也可以采用函数库中的fflush函数手动刷新缓冲区。

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


思考
- 可以知道函数的返回值如果小于nmemb则说明可能出现读取错误或者到达文件末尾,那应该如何区分这两种情况?
回答:可以通过标准库中提供的两个函数区分,一个函数是feof(),另一个则是ferror函数。


浙公网安备 33010602011771号