APUE知识点记录 【三】标准I/O库

通过文件I/O读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销,标准I/O可以看成是在文件I/O的基础上封装了缓冲机制。先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数。

int fflush(FILE *fp);//如若fp是NULL,则次函数将导致所有输出流被冲洗。

1. 流与FILE对象

标准I/O库的所有操作都是围绕流(stream)进行的。当用标准I/O库打开一个或撞见一个文件时,我们已使一个流与文件关联。
ascii的一个字符用一个字节表示,而国际字符集的一个字符可用多个字符表示,成为宽字符集

当一个流最初被创建时,它并没有定向。如若在未定向的流上使用一个多字节I/O函数, 则将该流的定向设置为宽定向的。若在未定向的流上使用一个单字节I/O函数,则将该流的定向设为字节定向的。
只有两个函数可改变流的定向。freopen函数清除一个流的定向;fwide函数可用于设置流的定向。

#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);
//返回值:若流是宽定向的,返回正值;若流是字节定向的,返回负值;若流是未定向的,返回0
  • 如若mode参数值为负,fwide将试图使指定的流是字节定向的。
  • 如若mode参数值为正,fwide将试图使指定的流是宽定向的。
  • 如若mode参数值为0,fwide将不试图设置流的定向,但返回标识该流定向的值。

注意,fwide并不改变已定向流的方向。
还应注意,fwide无出错返回。 可以在调用前先清除errno,从fwide返回时检查errno的值。

2 标准输入、标准输出与标准错误

文件描述符分别:STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO
可以通过头文件<stdio.h>中定义的文件指针stdin、stdout和stderr加以引用。

3 缓冲

讲解缓冲之前需要先明白冲洗(flush),它是将缓冲区的内容写入磁盘中(即使缓冲区未填满)。
缓冲的作用:减少系统read和write的次数。
标准IO提供3中类型的缓冲:

  1. 全缓冲:在填满标准I/O缓冲区后,才进行实际I/O操作。全缓冲使用malloc来分配缓冲区
  2. 行缓冲:在输入或输出中遇到了换行符或者缓冲区被填满,才进行实际I/O操作。例如用getc读取一个字符时,实际读取了一行在缓冲区中。(注:当标准I/O库不使用缓冲或者使用行缓冲需要从内核读取数据时,行输出缓冲会被flush)
  3. 不带缓冲

一般缓冲区类型特征是:

  • 当且仅当标准输入和标准输出并不指向交互式设备时,它们才是全缓冲的。 (iso c)
  • 标准错误绝不会是全缓冲的,通常是不带缓冲的,这就使得出错信息可以尽快显示出来,而不管它们 是否含有一个换行符。
  • 若是指向终端设备的流,则是行缓冲的;否则是全缓冲的。(很多系统默认)

调用下列两个函数可以更改缓冲类型。

#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
//返回值:若成功,返回0;若出错,返回非0

这两个函数一定要在流被打开后、执行任何一个其他操作之前使用。
  对于setbuf来说,如果buf参数为空,则关闭数据流的缓冲功能;如果buf的值不为空,指向一个缓冲区,那么就将缓冲类型设置为全缓冲(一些系统实现了当数据流来自Terminal时,设置为行缓冲)
  对于setvbuf的_IPFBF和_IOLBF,如果buf为空,则为系统默认缓冲区,如果buf不为空,则使用用户自定义的缓冲区。

使用setvbuf,我们可以精确地说明所需的缓冲类型。这是用mode参数实现的:
_IOFBF 全缓冲
_ IOLBF 行缓冲
_IONBF 不带缓冲
具体buf、mode、size参数选择如下图。
在这里插入图片描述

4. 打开流

#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type);
//3个函数的返回值:若成功,返回文件指针;若出错,返回NULL
  1. fopen函数打开路径名为pathname的一个指定的文件
  2. freopen函数在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。若该流已经定向,则使用 freopen清除该定向。此函数一般用于将一个指定的文件打开为一个预定的流:标准输入、标准输出或标准错误
  3. fdopen函数取一个已有的文件描述符(可能从open、dup、dup2、fcntl、pipe、socket、socketpair或accept 函数得到此文件描述符),并使一个标准的I/O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数返回的描述符。 因为这些特殊类型的文件不能用标准I/O函数fopen打开,所以我们必须先调用设备专用函数以获得一个文件描述符,然后用 fdopen使一个标准I/O流与该描述符相结合。

参数type制定对IO流的读写方式。
其中b的作用是区分文本文件和二进制文件——这在unix系统中毫无意义。
+的作用是表示读和写类型
在这里插入图片描述
对于fdopen,因为文件描述符已打开,所以fdopen为写而打开并不截短该文件。另外,标准IO添加(追加写)方式也不能用于创建文件,因为如果一个文件描述符引用一个文件则该文件一定已经存在。
若使用读和写类型打开一个文件(+),有以下限制:

  • 若中间没有fflush、fseek、fsetpos或rewind,则在输出之后不能直接跟输入。
  • 若中间没有fseek、fsetpos或rewind,或者一个输入操作没有到文件尾端,则输入之后不能直接跟输出。
    在这里插入图片描述

用fclose关闭一个打开的流,文件关闭之前对于缓冲区中flush输出数据,丢弃输入数据,然后释放缓冲区。

#include <stdio.h>
int fclose(FILE *fp);
//返回值: 成功0,出错EOF

5. 读和写流

对于打开的流有三种I/O读写操作可以选择:

  1. 每次读或写一个字符,若带有缓冲则有标准I/O函数处理所有缓冲。
  2. 每次读或写一行,使用fgets和fputs,以一个换行符终止。fgets应说明能处理的最大行长。
  3. 直接I/O,使用fread和fwrite,每次读或写某个数量的对象,每个对象具有制定的长度,常用语从二进制文件中读写一个结构。

5.1 输入函数

#include <stdio.h>
int gets(FILE *fp);     //宏实现,快
int fgetc(FILE *fp);    //函数实现,可作为参数给另一个参数
int getchar(void); //等同于getc(stdin);
//若成功返回下一个字符;若出错或到达尾端,返回EOF

  返回值将字符从unsigned char转化为int型是为了兼容EOF(该值通常是-1)。
  为了区分出现EOF时究竟是出错还是到达尾端,需要调用ferrorfeof

#include <stdio.h>
int ferror(FILE *fp);  //fp中标记为error返回非0,eof返回0
int feof(FILE *fp);    //fp中标记为error返回0,eof返回非0
void clearerr(FILE *fp);//清除标记

从流中读取数据后可以用回送字符操作int ungetc(int c, FILE *fp);将字符再次压入流中,并且可以清楚文件结束标志。回送字符操作经常用于进行切词或几号切分操作时,先看一下下一个字符再决定如何处理当前字符

5.2 输出函数

#include <stdio.h>
int puts(int c, FILE *fp);     //宏实现,快
int fputc(int c, FILE *fp);    //函数实现,可作为参数给另一个参数
int getchar(int c); //等同于putc(int c, stdin);
//若成功, 返回c;若出错,返回EOF

6 每次一行I/O

6.1每次输入一行

#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);//从指定流读
char *gets(char *buf);//从标准输入读取
//若成功,返回buf;若出错或到达尾端,返回NULL

buf指定了缓冲区的地址。
fgets必须指定缓冲的长度n,然后读取到换行符或超过n-1的字符为止。
gets无法指定缓冲区长度导致可能造成缓冲区溢出。并且gets不会讲换行符存入缓冲区。

6.2 每次输出一行

#include <stdio.h>
char *fputs(const char *restrict str, FILE *restrict fp);
char *puts(const char *str);
//若成功,返回非负值;若出错,返回EOF

两个函数都是将以null为终止符的字符串输出,切不写出null。但puts随后是再想标准输出写入一个换行符,而fputs需要自己写入换行符。

7 标准I/O的效率

猜你喜欢

转载自blog.csdn.net/RaKiRaKiRa/article/details/84403363