第五章 标准I/O库

来源:互联网 发布:园游会周杰伦 知乎 编辑:程序博客网 时间:2024/05/21 06:33
本章讲述的是标准I/O库的很多细节,如缓冲区的分配、以优化的块长度执行I/O等,因为有这些处理,让我们不必担心如何选择正确的块长度。

5.2 流和FILE对象

1.流定向决定了所读、写的字符是单字节还是多字节。当一个流最初被创建时,它并没有定向。如果在未定向的流上使用一个多字节I/O函数,则该流的定向设置为宽定向。如果在未定向的流上使用一个单字节I/O函数,则该流的定向设置为字节定向。
函数fwide()设置流的定向
//返回值: 若流是宽定向,返回正值;字节定向,返回负值,未定向返回0.

 int fwide(FILE *stream, int mode);

参数mode的值
如果mode的参数值为负,函数将使指定字节定向流;
如果mode的参数值为正,函数将使指定宽定向流;
如果mode的参数值为0,函数将试图不设置字节流,但返回标实该流定向的值;

5.4 缓冲

标准的I/O提供了3种类型的缓冲
1、全缓冲:即填满标准的I/O缓冲区后才进行实际的I/O操作
冲洗(flush):说明标准I/O缓冲区的写操作。缓冲区可由标准I/O例程自动冲洗
注意:unix环境中冲洗(flush)有两种意思:在标准I/O库方面。flush意味着将缓冲区的内容写到磁盘上,在终端驱动方面,flush(刷新)表示丢弃已存储在缓冲区中的数据。
2、行缓冲:当输入和输出遇到换行符时,标准I/O库执行I/O操作。
3、不带缓冲:标准I/O库不对字符进行缓冲存储。标准错误流通常情况下是不带缓冲的。
很多系统使用下列类型的缓冲:
1、标准出错时不带缓冲的。
2、若是指向终端设备的流,则是行缓冲的,否则是全缓冲。
更改缓冲类型:

//函数需在流已被打开后调用,成功返回0,出错返回非0void setbuf(FILE * restrict fp,  char * restrict buf  );//打开或关闭缓冲机制,参数buf必须是指向一个长度为BUFSIZE的缓冲区。为了关闭缓冲将buf设置为NULL。
int setvbuf(FILE  * restrict fp , char   *restrict  buf ,int mode, size_t size );//确切的说明所需的缓冲类型,由mode参数指定,如果指定一个不带缓冲的流则忽略buf和size参数,如果指定全缓冲或者行缓冲,则buf和size可选择的指定一个缓冲区及长度。

mode参数的取值:
_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不带缓冲

5.5打开流

成功返回文件指针,若出错返回NULLFILE * fopen (const char * restrict pathname, const char * restrict type);//打开一个路径名为pathname的文件FILE *freopen(const char  * restrict pathname, const char  *restrict type , FILE * restrict fp);//指定的流上打开一个文件,一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出、标准出错。FILE * fdopen( int fd,  const char * type)//取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。常用于创建管道或网络通信通道函数返回的描述符。

type参数—指定该I/O流的读写方式:r 或rb w或wb r+或rb+ a+或a+b或ab+

除非流引用终端设备,否则按照系统默认,流打开时是全缓冲的,若流引用终端设备,则该流是行缓冲的。

5.6读和写流

一旦打开了流,则可以在3种不同类型的非格式话I/O中进行选择,对其进行读、写操作。
1、一次一个字符的I/O。
2、每次一行的I/O 使用fgets和fputs函数。
3、直接I/O,fread和fwrite函数。

1、输入函数//3个函数可用于一次读写一个字符,成功返回下一个字符,出错或到达文件尾端,返回EOFint getc(FILE *fp);//宏函数int fgetc(FILE * fp);int getchar(void);

但是不管是出错或者到达文件尾端,这三个函数都返回同样的值,为了区分这两种情况,必须调用ferror和feof

//两个函数的返回值:若为真。返回非0,否则返回假(0);int ferror(FILE *fp);//出错标志int feof(FILE * fp);//文件结束标志

从流中读取数据后,可以调用ungetc将字符再压送回流中。

//成功返回c,出错返回EOFint ungetc(int c ,FILE * fp);压送回到流中的字符以后又可以从流中读出,但读出字符的顺序与压送回的顺序相反。

2.输出函数

上述的输入函数都对应一个输出函数int putc(int c, FILE * fp);//宏函数int fputc(int c , FILE *fp);int putchar(int c);

5.7 每次一行I/O

下面的两个函数提供每次输入一行的功能char * fgets(char * restrict buf , int n ,FILE * restrict fp);//从指定的流中读,将换行符存入缓冲区中char  * gets(char * buf); //从标准输入中读,但不推荐使用,由于不能指定缓冲区的长度,容易造成缓冲区溢出,并且不将换行符存入缓冲区中

每次输出一行

//若成功返回非负值,若出错,返回EOFint fputs(const char * restrict str ,FILE * restrict fp);//以null字节终止的字符串写到指定的流。int puts(const char *str);//以null字节终止的字符串写到标准输出,终止符不写出。但是puts会在结尾处自动添加一个换行符

5.9二进制I/O

执行二进制I/O操作的两个函数

//两个函数的返回值,读或写的对象数size_t fread (void * restrict ptr, size_t size, size_t nobj,FILE * restrict fp);size_t fwrite (void * restrict ptr, size_t size, size_t nobj,FILE * restrict fp);

使用二进制I/O的基本问题,它只能用于读在同一系统上已写的数据,在不同的系统下,这两个函数就可能不能正常工作因为:
1、在一个结构中,同一成员的偏移量可能随编译程序和系统的不同而不同(由于不同的对齐要求)。
2、用来存储多字节整数和浮点值的二进制格式在不同的系统结构间也可能不同。

5.10定位流

下面函数实现定位流:
//假定文件位置可以存放在一个长整型中
long ftell(FILE *fp);,返回值,成功返回当前文件位置指示,出错返回-1;
int fseek(FILE * fp, long offset, int whence);//成功返回0,出错返回-1,offset指定的字节偏移量,whence取SEEK_SET SEEK_CUP
SEEK_END

5.11格式化I/O
1格式化输出

int printf(const char * restrict format, ... );//将格式化数据写到标准输出    //成功返回输出的字符数;出错返回负值int fprintf(FILE * restrict fp , const char * restrict format, ...);//写到指定的流中                                                                                                      //成功返回输出的字符数;出错返回负值int dprintf(int fd, const char * restrict format , ....);//写到指定的文件描述符                                                                                      //成功返回输出的字符数;出错返回负值int sprintf(char * restrict  buf, const char * restrict format, ....);//格式化的字符送入数组,buf中,且在尾端加\0;                                                                                                               //成功返回存入的字符数,编码出错返回负值int snprintf(char *restrict buf, size_t n, const char * restrict format ,.....);//超过n值得所有字符都会被丢去。                                                                                                                             //成功返回存入的字符数,编码出错返回负值

2.格式化输入

//返回值,赋值的输入项数,出错或到达文件尾端返回EOFint scanf(const char * restrict format, ....);int fscanf(FILE * restrict  fp, const char  * restrict format, .....);int sscanf(const char * restrict  buf, const char * restrict format, ....);

5.12 实现细节

获取流的描述符
int fileno(FILE * fp);//返回值,与该流相关联的文件描述符

5.13临时文件

char * tmpnam(char *ptr);//返回指向唯一路径名的指针,产生一个与现有文件名不同的一个有效路径名的字符串,每次调用都产生一个不同的路径名,若ptr为NULL 则所产生的路径名存放在静态区中,后续调用tmpnam时会重写该静态区,如果不是NULL,则认为它应该是指向长度至少是L_tmpnam个字符的数组。

FILE *tmpfile(void);//成功返回文件指针,出错返回NULL,创建一个临时二进制文件,在关闭该文件或者程序结束时将自动删除这种问件。

处理临时文件:
两函数文件名字通过template字符串进行选择的,这个字符串的后六位设置为XXXXXX的路径名,成功则修改这些XXXXXX
char * mkdtemp(char * template);//成功返回指向目录的指针,出错返回NULL
//创建一个目录,该目录有唯一的一个名字

int mkstemp(char * template)//成功返回文件描述符,若出错返回-1;创建一个文件,该文件有唯一的一个名字

注意:与tempfile不同,mkstemp创建文件的临时文件并不会自动删除。如果希望从文件系统命名空间中删除该文件,必须自己对它解除链接。
使用tmpnam和tmpfile存在一个缺点:在返回唯一的路径名和用该名字创建文件之间存在一个时间窗口,在这一个时间窗口中,另一个进程可以用相同的名字创建该文件。

5.14内存流

内存流不适合存储二进制文件,因为二进制在数据尾端之前可能包含很多个NULL;
内存流的创建:
第一个函数
//成功返回流指针,出错返回NULL
FILE *fmemopen(void restrict buf, size_t size, const char *restrict type);//允许调用者提供缓冲区用于内存流,buf参数指向缓冲区的开始位置,size参数指定缓冲区大小的字节数,当流关闭时缓冲区会被释放。type参数控制如何使用流。type类型:r w w+ r+ a+。。。。。。

注意:type与标准I/O流中的参数取值基本一样,但是也存在一些差别:1、无论何时以追加写方式打开内流时,当前文件位置设为缓冲区中的第一个null字节。如果缓冲区中不存在null字节,则当前位置就设为缓冲区结尾的后一个字节。当流并不是以追加写的方式打开时,当前位置设为缓冲区的开始位置。因为追加写模式通过第一个null字节确定数据的尾端。
其他两个函数
FILE * open_memstream(char bufp,size_t size);//创建的流是面向字节

FILE * open_wmemstream(wchar_t **buf, size_t sizep)//创建的流是面向宽字节的
避免了缓冲区溢出,内存流适用于创建字符串。因为内存流只访问主存,不访问磁盘文件
思考:主存、内存、磁盘文件?
内存:速度快,容量小,断电后不保留,CPU可以直接访问。
外存:容量大,能长期保留,CPU不可以直接访问,比如光盘、U盘。
主存:它可以理解为我们电脑主机上的“硬盘”我们需要长期保存的东西都存在它上面,因为它属于ROM断电后的面存储的资料不会丢失。

0 0
原创粉丝点击