UNIX环境高级编程05.pdf
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《UNIX环境高级编程05.pdf》由会员分享,可在线阅读,更多相关《UNIX环境高级编程05.pdf(19页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、下载第5章标 准 I/O 库5.1 引言本章说明标准 I/O库。因为不仅在 U N I X而且在很多操作系统上都实现此库,所以它由ANSI C标准说明。标准I/O库处理很多细节,例如缓存分配,以优化长度执行I/O等。这样使用户不必担心如何选择使用正确的块长度(如3.9节中所述)。标准I/O库是在系统调用函数基础上构造的,它便于用户使用,但是如果不较深入地了解库的操作,也会带来一些问题。标准I/O库是由Dennis Ritchie在1 9 7 5年左右编写的。它是由Mike Lesk编写的可移植I/O库的主要修改版本。令人惊异的是,1 5年后制订的标准I/O库对它只作了极小的修改。5.2 流和F
2、 I L E对象在第3章中,所有I/O函数都是针对文件描述符的。当打开一个文件时,即返回一个文件描述符,然后该文件描述符就用于后读的 I/O操作。而对于标准 I/O库,它们的操作则是围绕流(s t r e a m)进行的(请勿将标准I/O术语流与系统V的STREAMS I/O系统相混淆)。当用标准I/O库打开或创建一个文件时,我们已使一个流与一个文件相结合。当打开一个流时,标准I/O函数f o p e n返回一个指向F I L E对象的指针。该对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际 I/O的文件描述符,指向流缓存的指针,缓存的长度,当前在缓存中的字符数,出错标
3、志等等。应用程序没有必要检验 F I L E对象。为了引用一个流,需将 F I L E指针作为参数传递给每个标准I/O函数。在本书中,我们称指向F I L E对象的指针(类型为F I L E*)为文件指针。在本章中,我们以U N I X系统为例,说明标准I/O库。正如前述,此标准库已移到除U N I X以外的很多系统中。但是为了说明该库实现的一些细节,我们选择U N I X实现作为典型进行介绍。5.3 标准输入、标准输出和标准出错对一个进程预定义了三个流,它们自动地可为进程使用:标准输入、标准输出和标准出错。在3.2节中我们曾用文件描述符S T D I N _ F I L E N O,S T
4、D O U T _ F I L E N O和S T D E R R _ F I L E N O分别表示它们。这三个标准I/O流通过预定义文件指针s t d i n,s t d o u t和s t d e r r加以引用。这三个文件指针同样定义在头文件中。5.4 缓存标准I/O提供缓存的目的是尽可能减少使用 r e a d和w r i t e调用的数量(见表3-1,其中显示了在不同缓存长度情况下,为执行 I/O所需的C P U时间量)。它也对每个I/O流自动地进行缓存管理,避免了应用程序需要考虑这一点所带来的麻烦。不幸的是,标准 I/O库令人最感迷惑的也是它的缓存。标准I/O提供了三种类型的缓存
5、:(1)全缓存。在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。对于驻在磁盘上的文件通常是由标准I/O库实施全缓存的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用m a l l o c(见7.8节)获得需使用的缓存。术语刷新(f l u s h)说明标准I/O缓存的写操作。缓存可由标准I/O例程自动地刷新(例如当填满一个缓存时),或者可以调用函数 ff l u s h刷新一个流。值得引起注意的是在 U N I X环境中,刷新有两种意思。在标准 I/O库方面,刷新意味着将缓存中的内容写到磁盘上(该缓存可以只是局部填写的)。在终端驱动程序方面(例如在第11章中所述的t c
6、 f l u s h函数),刷新表示丢弃已存在缓存中的数据。(2)行缓存。在这种情况下,当在输入和输出中遇到新行符时,标准 I/O库执行I/O操作。这允许我们一次输出一个字符(用标准 I/O fputc函数),但只有在写了一行之后才进行实际 I/O操作。当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。对于行缓存有两个限制。第一个是:因为标准 I/O库用来收集每一行的缓存的长度是固定的,所以只要填满了缓存,那么即使还没有写一个新行符,也进行 I/O操作。第二个是:任何时候只要通过标准输入输出库要求从(a)一个不带缓存的流,或者(b)一个行缓存的流(它预先要求从内核得到数据)得到输
7、入数据,那么就会造成刷新所有行缓存输出流。在(b)中带了一个在括号中的说明的理由是,所需的数据可能已在该缓存中,它并不要求内核在需要该数据时才进行该操作。很明显,从不带缓存的一个流中进行输入((a)项)要求当时从内核得到数据。(3)不带缓存。标准I/O库不对字符进行缓存。如果用标准 I/O函数写若干字符到不带缓存的流中,则相当于用 w r i t e系统调用函数将这些字符写至相关联的打开文件上。标准出错流s t d e r r通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。ANSI C要求下列缓存特征:(1)当且仅当标准输入和标准输出并不涉及交互作用设备时,
8、它们才是全缓存的。(2)标准出错决不会是全缓存的。但是,这并没有告诉我们如果标准输入和输出涉及交互作用设备时,它们是不带缓存的还是行缓存的,以及标准输出是不带缓存的,还是行缓存的。S V R 4和4.3+B S D的系统默认使用下列类型的缓存:标准出错是不带缓存的。如若是涉及终端设备的其他流,则它们是行缓存的;否则是全缓存的。对任何一个给定的流,如果我们并不喜欢这些系统默认,则可调用下列两个函数中的一个更改缓存类型:#include void setbuf(FILE*f p,char*b u f);int setvbuf(FILE*f p,char*b u f,int m o d e,size
9、_t s i z e);返回:若成功则为0,若出错则为非0这些函数一定要在流已被打开后调用(这是十分明显的,因为每个函数都要求一个有效的文件9 2U N I X环境高级编程下载指针作为它们的第一个参数),而且也应在对该流执行任何一个其他操作之前调用。可以使用s e t b u f函数打开或关闭缓存机制。为了带缓存进行 I/O,参数buf 必须指向一个长度为B U F S I Z的缓存(该常数定义在中)。通常在此之后该流就是全缓存的,但是如果该流与一个终端设备相关,那么某些系统也可将其设置为行缓存的。为了关闭缓存,将 b u f设置为N U L L。使用s e t v b u f,我们可以精确地
10、说明所需的缓存类型。这是依靠m o d e参数实现的:_IOFBF 全缓存_IOLBF 行缓存_IONBF 不带缓存如果指定一个不带缓存的流,则忽略buf 和size 参数。如果指定全缓存或行缓存,则buf 和s i z e可以可选择地指定一个缓存及其长度。如果该流是带缓存的,而 buf 是N U L L,则标准I/O库将自动地为该流分配适当长度的缓存。适当长度指的是由 s t r u c t结构中的成员s t _ b l k s i z e所指定的值(见4.2节)。如果系统不能为该流决定此值(例如若此流涉及一个设备或一个管道),则分配长度为B U F S I Z的缓存。伯克利系统首先使用s
11、t _ b l k s i z e表示缓存长度。较早的系统V版本使用标准I/O常数B U F S I Z(其典型值是1 0 2 4)。即使4.3+B S D使用s t _ b l k s i z e决定最佳的I/O缓存长度,它仍将B U F S I Z设置为1 0 2 4。表5-1列出了这两个函数的动作,以及它们的各个选择项。表5-1 setbuf 和setvbuf 函数函数m o d eb u f缓存及长度缓存的类型s e t b u fn o n n u l l长度为B U F S I Z的用户缓存全缓存或行缓存N U L L(无缓存)不带缓存_ I O F B Fn o n n u l
12、l长度为s i z e的用户缓存全缓存N U L L合适长度的系统缓存s e t v b u f_ I O L B Fn o n n u l l长度为s i z e的用户缓存行缓存N U L L合适长度的系统缓存_ I O N B F忽略无缓存不带缓存要了解,如果在一个函数中分配一个自动变量类的标准 I/O缓存,则从该函数返回之前,必须关闭该流。(7.8节将对此作更多讨论。)另外,S V R 4将缓存的一部分用于它自己的管理操作,所以可以存放在缓存中的实际数据字节数少于 s i z e。一般而言,应由系统选择缓存的长度,并自动分配缓存。在这样处理时,标准I/O库在关闭此流时将自动释放此缓存。任
13、何时候,我们都可强制刷新一个流。#i n c l u d e int fflush(FILE*f p);返回:若成功则为0,若出错则为E O F第5章标 准 I/O 库9 3下载此函数使该流所有未写的数据都被传递至内核。作为一种特殊情形,如若 f p是N U L L,则此函数刷新所有输出流。传送一个空指针以强迫刷新所有输出流,这是由ANSI C新引入的。非ANSI C库(例如较早的系统V版本和4.3 B S D)并不支持此种特征。5.5 打开流下列三个函数可用于打开一个标准I/O流。#include FILE*fopen(const char*p a t h n a m e,const cha
14、r*t y p e);FILE*freopen(const char*p a t h n a m e,const char*t y p e,FILE*f p);FILE*fdopen(int f i l e d e s,const char*t y p e);三个函数的返回:若成功则为文件指针,若出错则为 N U L L这三个函数的区别是:(1)fopen打开路径名由pathname 指示的一个文件。(2)freopen在一个特定的流上(由f p指示)打开一个指定的文件(其路径名由pathname 指示),如若该流已经打开,则先关闭该流。此函数一般用于将一个指定的文件打开为一个预定义的流:标准
15、输入、标准输出或标准出错。(3)fdopen取一个现存的文件描述符(我们可能从 o p e n,d u p,d u p 2,f c n t l或p i p e函数得到此文件描述符),并使一个标准的I/O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数获得的插述符。因为这些特殊类型的文件不能用标准 I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用f d o p e n使一个标准I/O流与该描述符相结合。f o p e n和f r e o p e n是ANSI C的所属部分。而ANSI C并不涉及文件描述符,所以仅有P O S I X.1具有f d o
16、 p e n。t y p e参数指定对该I/O流的读、写方式,ANSI C规定t y p e参数可以有1 5种不同的值,它们示于表5-2中。表5-2 打开标准I/O流的t y p e参数t y p e说明r 或 r b为读而打开w 或 w b使文件成为0长,或为写而创建a 或 a b添加;为在文件尾写而打开,或为写而创建r+或 r+b 或 r b+为读和写而打开w+或 w+b 或 w b+使文件为0长,或为读和写而打开a+或 a+b 或 a b+为在文件尾读和写而打开或创建9 4U N I X环境高级编程下载使用字符b作为t y p e的一部分,使得标准 I/O系统可以区分文本文件和二进制文件
17、。因为U N I X内核并不对这两种文件进行区分,所以在 U N I X系统环境下指定字符 b作为t y p e的一部分实际上并无作用。对于f d o p e n,t y p e参数的意义则稍有区别。因为该描述符已被打开,所以 f d o p e n为写而打开并不截短该文件。(例如,若该描述符原来是由 o p e n函数打开的,该文件那时已经存在,则其O _ T R U N C标志将决定是否截短该文件。f d o p e n函数不能截短它为写而打开的任一文件。)另外,标准I/O添加方式也不能用于创建该文件(因为如若一个描述符引用一个文件,则该文件一定已经存在)。当用添加类型打开一文件后,则每次
18、写都将数据写到文件的当前尾端处。如若有多个进程用标准I/O添加方式打开了同一文件,那么来自每个进程的数据都将正确地写到文件中。4.3+B S D以前的伯克利版本以及K e r n i g h a n和R i t c h i e1 9 8 81 7 7页上所示的简单版本并不能正确地处理添加方式。这些版本在打开流时,调用 l s e e k到达文件尾端。在涉及多个进程时,为了正确地支持添加方式,该文件必须用 O _ A P P E N D标志打开,我们已在3.3节中对此进行了讨论。在每次写前,做一次 l s e e k操作同样也不能正确工作(如同在3.11节中讨论的一样)。当以读和写类型打开一文件
19、时(t y p e中+号),具有下列限制:如果中间没有ff l u s h、f s e e k、f s e t p o s或r e w i n d,则在输出的后面不能直接跟随输入。如果中间没有f s e e k、f s e t p o s或r e w i n d,或者一个输出操作没有到达文件尾端,则在输入操作之后不能直接跟随输出。按照表5-2,我们在表5-3中列出了打开一个流的六种不同的方式。表5-3 打开一个标准I/O流的六种不同的方式限制rwar+w+a+文件必须已存在擦除文件以前的内容流可以读流可以写流只可在尾端处写注意,在指定w或a类型创建一个新文件时,我们无法说明该文件的存取许可权位
20、(第 3章中所述的o p e n函数和c r e a t函数则能做到这一点)。P O S I X.1要求以这种方式创建的文件具有下列存取许可权:S _ I R U S RS _ I W U S RS _ I R G R PS _ I W G R PS _ I R O T HS _ I W O T H除非流引用终端设备,否则按系统默认,它被打开时是全缓存的。若流引用终端设备,则该流是行缓存的。一旦打开了流,那么在对该流执行任何操作之前,如果希望,则可使用前节所述的s e t b u f和s e t v b u f改变缓存的类型。调用f c l o s e关闭一个打开的流。#include int
21、 fclose(FILE*f p);第5章标 准 I/O 库9 5下载返回:若成功则为0,若出错则为E O F在该文件被关闭之前,刷新缓存中的输出数据。缓存中的输入数据被丢弃。如果标准 I/O库已经为该流自动分配了一个缓存,则释放此缓存。当一个进程正常终止时(直接调用e x i t函数,或从m a i n函数返回),则所有带未写缓存数据的标准I/O流都被刷新,所有打开的标准I/O流都被关闭。5.6 读和写流一旦打开了流,则可在三种不同类型的非格式化 I/O中进行选择,对其进行读、写操作。(5.11节说明了格式化I/O函数,例如p r i n t f和s c a n f。)(1)每次一个字符的I
22、/O。一次读或写一个字符,如果流是带缓存的,则标准I/O函数处理所有缓存。(2)每次一行的I/O。使用f g e t s和f p u t s一次读或写一行。每行都以一个新行符终止。当调用f g e t s时,应说明能处理的最大行长。5.7节将说明这两个函数。(3)直接I/O。f r e a d和f w r i t e函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。5.9节将说明这两个函数。直接I/O(direct I/O)这个术语来自ANSI C标准,有时也被称为:二进制 I/O、一次一个对象I/O、面向记录
23、的I/O或面向结构的I/O。5.6.1 输入函数以下三个函数可用于一次读一个字符。#include int getc(FILE*f p);int fgetc(FILE*f p);int getchar(void);三个函数的返回:若成功则为下一个字符,若已处文件尾端或出错则为 E O F函数g e t c h a r等同于g e t c(s t d i n)。前两个函数的区别是g e t c可被实现为宏,而f g e t c则不能实现为宏。这意味着:(1)getc的参数不应当是具有副作用的表达式。(2)因为f g e t c一定是个函数,所以可以得到其地址。这就允许将 f g e t c的地址
24、作为一个参数传送给另一个函数。(3)调用f g e t c所需时间很可能长于调用g e t c,因为调用函数通常所需的时间长于调用宏。检验一下头文件的大多数实现,从中可见g e t c是一个宏,其编码具有较高的工作效率。这三个函数以unsigned char 类型转换为i n t的方式返回下一个字符。说明为不带符号的理由是,如果最高位为1也不会使返回值为负。要求整型返回值的理由是,这样就可以返回所有可能的字符值再加上一个已发生错误或已到达文件尾端的指示值。在 中的常数E O F被要9 6U N I X环境高级编程下载求是一个负值,其值经常是 1。这就意味着不能将这三个函数的返回值存放在一个字符
25、变量中,以后还要将这些函数的返回值与常数E O F相比较。注意,不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用f e r r o r或f e o f。#include int ferror(FILE*f p);int feof(FILE*f p);两个函数返回:若条件为真则为非0(真),否则为0(假)void clearerr(FILE*f p);在大多数实现的F I L E对象中,为每个流保持了两个标志:出错标志。文件结束标志。调用c l e a r e r r则清除这两个标志。从一个流读之后,可以调用u n g e t c将字符再送回流中。#inc
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- UNIX 环境 高级 编程 05
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内