
APUE 第五章 标准I/O库
第五章 标准I/O库
5.2 流和FILE对象
对于标准I/O库,它们的操作是围绕 流(stream)展开的。当用标准I/O库打开或者创建一个文件时,我们已使一个流与一个文件相关联。
流有单字节流和多字节流之分,决定一个流的类型的操作称为流的定向(orientation)。
freopen
函数清除一个流的定向;fwide
函数设置一个流的定向
1 |
|
根据 mode
的不同值,fwide
函数有不同的行为
mode
为正,函数试图将流定向为宽字节mode
为负,函数视图将流定向为单字节mode
为0,函数什么也不做,并返回标识该流定向的值
FILE
结构体是标准I/O库里最重要的一个结构体,指向它的指针 FILE *
称为文件指针
5.3 标准输入、标准输出和标准错误
标题里的三个流是为每个进程预定义的,分别通过文件指针 stdin
、stdout
、和 stderr
引用,它们定义在 <stdio.h>
头文件中
5.4 缓冲
缓冲一般都是为了提高效率,标准I/O的缓冲也一样,是为了减少 read
和 write
的调用次数
标准I/O提供三种缓冲
- 全缓冲。缓冲区全部填满后才冲洗(flush)。冲洗就是将缓冲区的内容写到磁盘
- 行缓冲。输入输出中出现换行符时执行冲洗。当然,这个行不能太长,要是缓冲区全部填满了还没有出现换行符,也是要执行冲洗的。
- 不带缓冲。
stderr
通常是不带缓冲的。
不同的流的缓冲类型一般是下面这样的:
- 标准错误是不带缓冲的
- 若是指向终端设备的流,则是行缓冲的;否则是全缓冲的
如果不想使用上面默认的缓冲方式,可以使用下面的函数更改缓冲类型
1 |
|
这两个函数都应该在 流已经打开 且 未进行任何其他操作之前调用
怎么用?
setbuf
函数可以打开或关闭缓冲。buf
指向缓冲区,则打开缓冲;buf
为 NULL
,则关闭缓冲
setvbuf
可以使用 mode
参数说明缓冲类型
mode |
缓冲类型 |
---|---|
_IOFBF | 全缓冲 |
_IOLBF | 行缓冲 |
_IONBF | 不带缓冲 |
size
参数是用来指定缓冲区长度的
由于这两个函数的行为组合有点多,使用下面的表格进行说明

怎么强制冲洗一个流?
使用 fflush
函数
1 |
|
5.5 打开流
有三个函数可以打开一个流
1 |
|
三个函数区别如下:
fopen
用于打开文件名指定的文件freopen
用于在一个指定的流上打开指定的文件fdopen
用于打开文件描述符指定的文件
type
参数指定打开流的方式(读/写/附加)

调用 fclose
关闭一个打开的流
1 |
|
5.6 对一个流进行 读 和 写
输入函数
一次读取一个字符的函数
1
2
3
4
5
6
7
8
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
//All three return: next character if OK, EOF on end of file or errorgetchar
相当于getc(stdin)
。前两个函数的区别在于,getc
可以实现为宏,而fgetc
是一个真正的函数可以看到,不论是出错还是到达文件末尾,那三个读函数都返回
EOF
,要想区分到底是哪种情况,还需调用下面这几个函数1
2
3
4
5
6
7
8
int ferror(FILE *fp);
int feof(FILE *fp);
//Both return: nonzero (true) if condition is true, 0 (false) otherwise
void clearerr(FILE *fp);clearerr
用于清除FILE
文件中维护的 出错 和 文件结束 这两个标志位
如果读到了不满意的字符,想退货,把它再送回流中,可以使用下面这个函数
1 |
|
输出函数
1
2
3
4
5
6
7
8
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
//All three return: c if OK, EOF on error它们的差别和上面的三个读取函数的差别是一样的
5.7 每次一行的 I/O
每次输入一行的函数
1 |
|
gets
函数不推荐使用
fgets
总是将换行符写入缓冲区,而 gets
则是从不写入换行符
每次输出一行的函数
1 |
|
总是推荐使用 fgets
和 fputs
5.9 二进制 I/O
下面两个函数执行二进制 I/O 操作
1 |
|
5.10 定位流
有三种方法定位标准 I/O 流
ftell
和fseek
函数。它们假定偏移量可以存储在一个长整型中ftello
和fseeko
函数。使用off_t
类型 代替了上面的长整型fgetpos
和fsetpos
函数。
1 |
|
ftell
返回指定文件的偏移量, fseek
则用来设置偏移量
1 |
|
这俩函数除了将 long
类型 换成 off_t
类型外,与 ftell
和 fseek
没有区别
fgetpos
和 fsetpos
俩函数是 ISO C 标准引入的
1 |
|
5.11 格式化 I/O
格式化输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
//All three return: number of characters output if OK, negative value if output error
int sprintf(char *restrict buf, const char *restrict format, ...);
//Returns: number of characters stored in array if OK, negative value if encoding error
int snprintf(char *restrict buf, size_t n,const char *restrict format, ...);
//Returns: number of characters that would have been stored in array
//if buffer was large enough, negative value if encoding errorprintf
写入到标准输出,fprintf
写入到指定的流,dprintf
写入到文件描述符指定的文件;sprintf
写入到指定的buf
中;snprintf
也是写入到指定的buf
中 ,但是它指定了buf
的大小
下面介绍如何编写转换说明。所谓转换说明,就是以上函数中的 `format` 函数,它决定了以什么样的格式显示后面的参数
转换说明有四个可选部分
1 | [flags][fldwidth][precision][lenmodifier]convtype |
flags
可选:

fldwidth
说明最小字段宽度
precision
以 .
开始,说明需要打印的精度
lenmodifier
可选以下值

convtype
不是可选的,而是必选的

格式化输入
1
2
3
4
5
6
7
8
9
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);
//All three return: number of input items assigned,
//EOF if input error or end of file before any conversion格式化输入同样需要转换说明
1
[*][fldwidth][m][lenmodifier]convtype
这里只对
convtype
进行说明
5.12 实现细节
归根结底,标准I/O库还是要调用第三章所介绍的函数,但是第三章里的函数都是用 文件描述符 标识文件,而本章的函数大多使用 FILE
结构体,如何建立这两者之间的联系呢?答案是使用 fileno
函数
1 |
|
5.13 临时文件
1 |
|
tmpfile
创建一个临时二进制文件,在关闭该文件或程序结束时将自动删除这种文件
SUS 为处理临时文件定义了另外两个函数:
1 |
|
5.14 内存流
内存流的特征:没有底层文件,所有的 I/O 操作都是通过缓存和主存之间的字节运输完成的
介绍第一个创建内存流的函数
1 |
|