文件(file)是存储器存储信息的区域。一般保存在一些永久介质中,如硬盘、U盘、DVD等。
我们编写的.c源文件也是文件,编译器在编译时需要先打开.c文件并读取其中的内容,当编译器处理完后会将其关闭。
从低层实现上看,C可以使用主机操作系统中的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层I/O。
由于不同的操作系统的底层I/O实现是相同的,所以C标准不可能为它们创建标准库,也不能这么做。于是,C通过C标准I/O包来处理文件,C标准中有用于处理文件的标准模型和一套标准I/O函数,它通过调用底层I/O来完成具体的文件操作。这样,不同的操作系统可以有不同的底层I/O实现,用户通过使用C标准的文件处理模型和标准I/O函数,就可以获得统一的使用界面。
操作系统的差异导致它有不同的底层I/O实现,这里的差异是多方面的,如系统存储文件的方式的不同,在有的系统上把文件的内容储存在一处,而文件的相关信息存储在另一处;在另一些系统上,则在文件中创建一份文件描述。在处理文件方面,不同的系统也可能表现出差异,如有的系统使用单个换行符标记行末尾,有的系统可能使用回车符和换行符的组合来表示行末尾。
但是使用的C标准I/O包,都不用考虑这些差异。 不管系统实际使用哪一种方式来表示行末尾,标准I/O函数这些表示法中互相转换。
C程序处理的是流,而不是直接处理文件。流是一个实际输入或输出映射的理想化数据流,意味着不同属性和不同种类的输入,将由属性统一的流来表示。打开文件的过程就是把流与文件相关联,而且对文件的读写都是通过流来完成。
C把输入和输出设备都视为存储设备上的普通文件,特别是把键盘和显示设备看作是每个C程序自动打开的文件,stdout流表示屏幕输出,stdin流表示键盘输入。getchar、putchar、printf、scanf函数都是标准I/O包中的成员,用于处理这两个流。
C用处理文件的方式处理键盘输入。程序读文件时需要检测文件的末尾才知道在什么地方停止。C的输入函数内置了文件结尾检测器。
计算机操作系统必须要以某种具体方式判断文件的开始和结束。一种方式是在文件末尾放一个特殊的字符标记文件结尾。另一种方式是存储文件的大小的信息,Unix使用这种方法处理所有的文件。但无论操作系统使用何种方法检测文件结尾。在C语言中,用getchar读取文件检测到文件结尾时将会返回一个特殊的值,即EOF(end-of-file的缩写)。这个值定义在stdio.h中:
#define EOF (-1)
为什么是-1呢?因为getchar函数的返回值都介于0~127之间,每个值对应标准字符集。如果系统能够识别扩展字符集,返回值可能介于0~255之间,但无论如何-1都不对应任何字符所以该值可用于标记文件结尾。
程序可以通过两种方式使用文件。一种方式是显式使用特定的函数打开文件、关闭文件、读取文件、写入文件等。另一种方式是设计能与键盘和屏幕互动的程序。通过不同的渠道重定向输入到文件和从文件输出。即把stdin流重新赋给文件。重定向特性与操作系统有关,与C无关。unix、linux、windows等都有重定向特性:
使用文件与stdin流相关联:
程序 < 文件
把程序的输出重定向到文件,把stdout显示设备赋给文件:
程序 > 文件
