#C Primer Plus: 第十三章 文件输入/输出
13.1 与文件进行通信
-
文件通常是在磁盘或固态磁盘上的一段已命名的储存区。
-
C提供两种文件模式:文本模式和二进制模式。
-
区别:文本文件\二进制文件
- 使用二进制编码的字符(如ASCII或Unicode)表示文本,该文件就是文本文件,其中包含文本内容。
- 使用二进制值代表机器语言代码或者数值数据或图片或音乐编码,该文件就是二进制文件,其中包含二进制内容。
-
区别:文本文件格式\二进制文件格式
-
区别:文本模式\二进制模式
- 文本模式中,程序所见的内容和文件的实际内容不同。程序以文本模式读取文件时,把本地环境表示的行尾或文件结尾映射为C模式。
- 二进制模式中,程序可以访问文件的每个字节。
-
I/O的级别:可以选择I/O的两个级别,底层I/O使用操作系统提供的基本I/O服务。标准高级I/O使用C库的标准包stdio.h头文件定义。因为无法保证所有的操作系统都使用相同的底层I/O模型,C标准只支持标准I/O包。
13.2 标准I/O
- 于底层I/O相比,标准I/O的好处:
- 可移植性
- 许多专门的函数简化了处理不同I/O的问题。
- 缓冲。一次转移一大块信息而不是一字节信息。
- exit()函数关闭所有打开的文件并结束程序。
- 标准要求0或宏EXIT_FAILURE用于表明成功结束程序,宏EXIT_SUCCESS用于表明结束程序失败。这些宏和exit()原型都位于stdlib.h头文件。
- return 和exit()的另一个区别是,即使在其他函数中(除main()以外)调用exit()也能结束整个程序。
- fopen()的模式字符串:
- 文本模式下:
- “r”: 以读模式打开文件
- “w”: 以写模式打开文件,把现有文件的长度截为0,如果文件不存在,则创建一个新文件。
- “a”: 以写模式打开文件,在现有文件末尾添加内容,如果文件不存在,则创建一个新文件。
- “r+”: 以更新模式打开文件,即可以读写文件。
- “w+”: 以更新模式打开文件,即读和写。如果文件存在,则将长度截为0,如果文件不存在,则创建一个新文件。
- “a+”: 以更新模式打开文件,即读和写。在现有文件的末尾添加内容,如果文件不存在则创建一个新文件;可以读整个文件,但只能从末尾添加内容。
- “rb”:在上述模式字符串末尾添加’b’则是以二进制模式打开文件,而不是文本模式。
- 程序成功打开文件后,fopen()将返回文件指针,其他I/O函数可以使用这个指针指定该文件。文件指针的类型是指向FILE的指针,FILE是一个定义在stdio.h中的派生类型。
- 文件指针fp并不指向实际的文件,而是指向一个包含文件信息的数据对象,其中包含操作文件的I/O函数所用的缓冲区信息。因为标准库中的I/O函数使用缓冲区,所以不仅需要知道缓冲区的位置,还要知道缓冲区被填充的程度以及操作哪一个文件。标准I/O函数根据这些信息在必要时决定再次填充或清空缓冲区。fp指向的数据对象包含这些信息。该数据对象是一个C结构。
- 如果getc()函数在读取一个字符时发现是文件末尾,它将返回一个特殊值EOF。
- fclose(fp)函数关闭fp指定的文件,必要时刷新缓冲区。正式的写法是判断是否成功关闭文件,如果关闭成功,fclose()函数返回0,否则返回EOF。
13.5 随机访问fseek()和ftell()
fseek():
- fseek()函数可以把文件看做数组,在fopen()打开的文件中直接移动到任意字节处。共有三个参数。
- 第一个参数是FILE指针,指向待查找的文件,fopen()应该已经打开该文件。
- 第二个参数数偏移量。该参数表示从起始点开始要移动的距离。该参数必须是一个long类型的值。可以为正(前移)、负(后移)或0.
- 第三个参数是模式,该参数确定起始点。存在有:SEEK_SET(文件开始处)、SEEK_CUR(当前位置)、SEEK_END(文件末尾)
fseek(fp,0L,SEEK_SET); fseek(fp,-10L,seek_END);
- 如果正常,fseek()返回值为0,如果出现错误,其返回值为-1。
ftell():
- ftell()函数返回一个long类型的值,表示文件中的当前位置。
- last = ftell(fp);
### 13.6 标准I/O的机理
- fopen()函数不仅打开一个文件,还创建了一个缓冲区(在读写模式下会创建两个缓冲区)以及一个包含文件和缓冲区数据的结构。
- 这个结构通常包含一个指定流中当前位置的文件位置指示器。除此之外,还包含错误和文件结尾的指示器、一个指向缓冲区开始处的指针、一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)。
- 调用fscanf()\getc()\fgets()等,文件中的缓冲大小数据块就被拷贝到缓冲区中。缓冲区的大小因实现而异。
- 在初始化结构和缓冲区后,输入函数按要求从缓冲区读取数据。当输入函数发现已读完缓冲区中的所有字符时,会请求把下一个缓冲大小的数据块从文件拷贝到该缓冲区中。
- 输出函数以类似的方式把数据写入缓冲区。但缓冲区被填满时,数据将被拷贝至文件中。
### 13.7 其他标准I/O函数
- int fflush(FILE *fp)函数刷新缓冲区。
- fread()\fwrit()用于以二进制形式处理数据。