第五章 标准I/O库
5.2 流和 FILE 对象
- 普通 I/O,文件描述符与文件关联
- 标准 I/O,流(stream)与文件关联
流的定向决定是单字节流还是 多字节流(宽)。
两个函数可以改变流的定向 fwide
和 freopen
。
- fwide
#include <wchar.h>
int fwide(FILE *stream, int mode);
mode参数:
- 参数为负:fwide 将试图指定流是字节定向
- 参数为正:fwide 将试图指定流是宽定向的
- 参数为 0:fwide 不设置流的定向,返回标识该流定向的值
返回值:
- 宽定向:“+”
- 字节定向: “-”
- 未定向: 0
fiwde
不改变已定向流的定向,且无出错返回。在调用前清除 errno
,返回值时检查 errno
的值,通过 errno
的值来判断成功还是失败。。
FILE 对象
fopen
打开一个流时,返回一个 FILE
对象的指针,FILE
对象是一个包含了流的所有信息的结构,包括文件描述符、缓冲区指针、缓冲区长度、缓冲区字符数及出错标志等。
5.3 标准输入、标准输出和标准错误
流与文件的对应方式,与文件描述符与文件的对应方式相似。
头文件:<stdio.h>
流 | 文件I/O | 标准I/O |
---|---|---|
输入 | STDIN_FILENO | stdin |
输出 | STDOUT_FILENO | stdout |
错误 | STDERR_FILENO | stderr |
5.4 缓冲
目的: 减少使用 read
和 write
调用的次数,自动对每个 I/O 流进行缓冲管理。
全缓冲
标准 I/O 填满缓冲区后才进行实际的 I/O 操作。磁盘上的文件就是这样。流第一次进行I/O操作时使用 malloc
获得缓冲区。
冲洗(flush):缓冲区的写操作。缓冲区可由程序自动冲洗,也可调用 fflush
手动冲洗一个流。
flush两种含义:
- 将缓冲区内容写到磁盘
- 清除缓冲区中数据
行缓冲
在输入和输出中遇到换行符,才执行I/O操作,比如 fputc
一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。终端通常使用行缓冲
限制:
- 每一行缓冲区长度固定,缓冲区满,没有换行符也进行I/O操作
- 从不带缓冲的流,或者行缓冲的流获取数据,冲洗所有行缓冲输出流
不带缓冲
标准I/O库不对字符进行缓冲存储。例如,用 fputs
写15个字符到不带缓冲流中,字符立即输出。可能使用 write
直接写入相关文件中。
stderr
通常不带缓冲。
通常情况:
stderr
是不带缓冲的- 指向终端设备的流,是行缓冲,否则是全缓冲
更改缓冲类型
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
int setvbuf(FILE *stream, char *buf, int mode , size_t size);
- setbuf
关闭或打开缓冲机制。带缓冲(全缓冲,如果终端设备,可以为行缓冲)时,buf
指向长度为 BUFSIZ
(定义在 <stdio.h>
) 的缓冲区;关闭缓冲,buf
设置为 NULL
。
- setvbuf
利用 mode
参数,精确说明需要使用的缓冲类型。
- 二者区别
函数 | mode | buf | 缓冲区及长度 | 缓冲类型 |
setbuf | 非空 | 长度为 BUFSIZ 的用户缓冲区 buf | 全缓冲或行缓冲 | |
NULL | 无缓冲区 | 不带缓冲 | ||
setvbuf | _IOFBF | 非空 | 长度为 size 的用户缓冲区 buf | 全缓冲 |
NULL | 合适长度的系统缓冲区 buf | |||
_IOLBF | 非空 | 长度为 size 的用户缓冲区 buf | 行缓冲 | |
NULL | 合适长度的系统缓冲区 buf | |||
_IONBF | (忽略) | 无缓冲区 | 不带缓冲 |
自动分配的 size
长度的值通常是常量 BUFSIZ
指定的值。
注意:
- 如果在一个函数内分配一个自动变量类的标准I/O缓冲区,则从函数返回之前,必须关闭该流。
- 缓冲区的一部分还用来存放实现自己的管理操作信息,所以存放在缓冲区的实际字节数少于
size
。 - 一般应由系统选择缓冲区的长度,并自动分配缓冲区。这种情况下关闭流,标准I/O库自动释放缓冲区。
强制冲洗一个流
fflush
#include <stdio.h>
int fflush(FILE *stream);
返回值
- 成功:0
- 失败:
EOF
并 设置errno
值
此函数使流所有未写的数据传送至内核,作为例外,如果 fp
是 NULL
,则将导致所有的输出流被冲洗。
fflush
仅仅刷新用户空间的由 C 库 提供的缓冲。要保证数据被物理地存储到磁盘上,必须也刷新内核缓冲。例如,使用 sync(2)
或 fsync(2)
。
5.5 打开流
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *pathname, const char *mode, FILE *stream);
参数
mode | 描述 | open(2)对应标志 |
---|---|---|
r / rb | 读方式,流指向文件开始位置 | O_RDONLY |
w / wb | 写,将文件长度截断至0,如果不存在,则创建文件,流指向文件开始位置 | O_WRONLY | O_CREAT | O_TRUNC |
a / ab | 写,追加(文件尾),不存在则创建文件,流指向文件末尾 | O_WRONLY | O_CREAT | O_APPEND |
r+ / r+b / rb+ | 读、写,流指向开始位置 | O_RDWR |
w+ / w+b / wb+ | 读、写,将文件长度截断至0,如果不存在,则创建文件,流指向开始位置 | O_RDWR | O_CREAT | O_TRUNC |
a+ / a+b / ab+ | 读、写, 如果文件不存在,则创建文件,读文件从开始读,写文件则追加至文件尾 | O_RDWR | O_CREAT | O_APPEND |
打开二进制文件时使用 b
,在区分文本文件和二进制文件的系统中可以使得文件以合适的方式打开。但是在 UNIX
和 Linux
等遵循 POSIX
的系统中,对两种文件不进行区分,b
都被自动忽略。如果为了良好的可移植,最好加上 b
。
返回值
- 成功:返回指向文件的
FILE
对象指针 - 出错:返回
NULL
fopen
打开 pathname
指向的一个文件,并与一个流关联
fdopen
将一个流关联到已经存在的文件描述符 fd
,打开模式(mode
)必须与文件描述符 fd 的 open 模式相匹配,流的定为与 fd
的偏移量相同,错误和文件结束标记被清除。w
或 w+
打开的文件也不被截断,文件描述符不会被复制(dup
),在关闭由 fdope