GUN C中的流
当我们要对文件(在Linux环境中一切皆文件,包括硬件设备、资源等)进行操作(读、写、读写)时,必须连接文件或形成通信管道。这个过程称为打开文件。打开文件后可以进行读、写、读写操作。
打开的文件可以称作为流,或者是文件描述符。可以通过传递参数给实际执行操作的函数,告知它们操作哪些文件。一些函数期待操作流,一些函数设计时就指定了操作文件描述符。
当读取文件或写入完毕后需要关闭文件操作符(在进程结束时,内核会关闭该进程打开的所有文件描述符)。一旦关闭流或文件描述符时,就不能再对它进行操作了。
当想对文件进行操作时,必须从文件描述符或流这两种机制中选择其一,建立程序与文件之间的联系。文件描述符为int类型的数据对象,而流则为FILE * (stdio.h中定义)数据对象(一种struct数据结构,而非高级语言中的对象)。
文件描述符为输入和输出提供了一种原始的、底层的接口。文件描述符和流都可用于设备文件、管道或套接字文件以便于同其他进程或普通文件进行通信。但是,如果想要与特殊设备进行操作,必须选用设备描述符,因为他没有提供流方面的服务。在一些特殊的操作中输入输出也必须使用文件描述符进行操作,如非阻塞(或轮询)。
流提供了一种高级的接口(相当于文件描述符的底层接口),流接口处理所有类型的文件--唯一的例外是缓冲区。
使用流来进行操作(与实际操作相反)的主要优点是相对于文件描述符更丰富、更强大。文件描述符只提供了字符操作,而流则还提供了格式化输入输出(printf scanf),以及面向行和面向字符操作的函数。因为流是在文件描述符的基础之上进行封装,所以可以直接从流中提取文件描述符,或者直接对文件描述符进行底层操作。也可以仅打开一个文件描述符,而后在其之上创建一个与之相关联的流操作。通常在我们开发中除了要对字符进行操作,我们都应当使用流。在初学时如果不太清楚选择什么函数进行操作,那么最好选择格式化输入输出。
如果考虑兼容性,而非仅使用于GUN环境,那么也应该清楚文件描述符并不如流那样好移植,因为文件描述符大多定义在posix标准之中。
打开文件的一个属性是它的文件位置,它可以跟踪下一个字符在文件中的位置是读还是写。在GNU系统和所有POSIX上。一个系统,文件位置是一个整数,表示从文件开始时的字节数。
文件位置通常设置为打开文件时的起始位置,每当读取或写入字符时,文件位置就会递增。换句话说,对文件的访问通常是连续的。
普通文件允许在文件内的任何位置读写操作。其他类型的文件也可以允许这样做。允许这种情况的文件有时被称为随机访问文件。您可以使用在流上的fseek函数(见文件定位)或在文件描述符上的lseek函数来更改文件位置(见I / O原语)。如果您尝试更改不支持随机访问的文件中的文件位置,则会得到ESPIPE错误。
为append访问打开的流和描述符是专门用于输出的:对这些文件的输出总是按顺序追加到文件的末尾,而不管文件的位置如何。但是,文件位置仍然用于控制文件读取操作的位置。
如果仔细想想,会发现一些程序可以同时读取一个给定的文件。为了使每个程序能够以自己的速度读取文件,每个程序都必须有自己的文件指针,而其他程序所做的任何事情都不会影响到它。
事实上,每个文件的打开都会创建一个单独的文件位置。因此,如果您在同一个程序中两次打开一个文件,就会得到两个独立文件位置的流或描述符。
相反,如果您打开一个描述符,然后复制它以得到另一个描述符,那么这两个描述符共享相同的文件位置:更改一个描述符的文件位置将影响另一个描述符。