- 标准 IO 库的操作围绕流 (stream) 进行
- 流的定向(orientation)决定了所读、所写的字符是单字节还是多字节
- 流最初被创建时,没有定向
- 如果在未定向的流上使用一个多/单字节的 IO 函数,则将该留的定向设置为宽/字节定向
freopen
函数可以清除一个流的定向fwide
函数可以设置流的定向,但不改变已定向流的定向
- 缓冲:尽可能减少使用 read 和 write 系统调用的次数
- 缓冲类型
- 全缓冲:填满标准 IO 缓冲区后才进行实际 IO 操作
- 驻留在磁盘上的文件通常由标准 IO 库实施全缓冲
- flush 说明标准 IO 缓冲区的写操作,可以标准库自动 flush(如缓冲区填满)或者调用 fflush 进行 flush
- 行缓冲:输入和输入遇到换行符时,标准 IO 库执行 IO 操作
- 流涉及到一个终端时(如 stdin, stdout)时,通常使用行缓冲
- 行缓冲的限制
- 标准 IO 库收集每一行的缓冲区长度是固定的,如果缓冲区填满了即使还没遇到换行符也进行 IO 操作
- 任何时候只要通过标准 IO 从(a)一个不带缓冲的流或(b)一个行缓冲的流(需要从内核得到数据)得到输入数据,那么就会 flush 所有行缓冲输出流
- 不带缓冲:不对字符进行缓冲存储,stderr 通常不带缓冲
- 全缓冲:填满标准 IO 缓冲区后才进行实际 IO 操作
- 缓冲类型
- ISO C 要求的缓冲特征
- 当且仅当当前标准输入和标准输出不指向交互式设备时,他们才是全缓冲的
- 标准错误不能是全缓冲的,通常是不带缓冲
- 若是指向终端设备的流,则是行缓冲的,否则是全缓冲
setbuf
/setvbuf
可以修改缓冲类型setbuf
打开或关闭缓冲机制,设置的缓冲区长度为 BUFSIZ(由 stdio.h 定义),通常设置之后就是全缓冲的setvbuf
可以设置上述任何一种缓冲类型和缓冲区长度,也可以有标准 IO 库自动分配合适的缓冲区长度
- 打开流的函数:
fopen
,freopen
,fdopen
- type 参数控制打开流的选项
- 如果多个进程用标准 IO 追加写方式打开同一个文件,每个进程的数据都将正常地写到文件中
- 读写类型(+)打开文件时的限制
- 输出后面如果没有 fflush/fseek/fsetpos/rewind,不能直接跟随输入
- 如果读操作没有达到文件尾端,或没有 fseek/fsetpos/rewind 不能直接跟随输出
- fopen 无法控制文件的访问权限(RWX)
- 关闭流:
fclose
,flush 缓冲区中的输出数据,丢弃缓冲区的中的输入数据 - 当一个进程正常中止(exit 或从 main 函数返回),则所有写缓冲数据的标准 IO 流都被 flush,所有打开的标准 IO 流都被关闭
- 流的非格式化字符 IO
- 每次一个字符
- 输入 getc/fgetc/getchar
- getc 不应该是有副作用的表达式
- fgetc 一定是函数,可以作为参数传给其他函数
- 返回 int 需要和 EOF 做比较
- 将字符压回流 ungetc
- 回送的字符不一定是上次读到的字符
- 不能回送 EOF
- 输出 putc/fpuc/putchar
- 输入 getc/fgetc/getchar
- 每次一行
- 输入 gets/fgets
- fgets 遇到换行或者读到 n-1 个字符时返回
- 不推荐使用 gets,会造成缓冲区溢出问题
- 输出 puts/fputs
- fputs 不写终止符(null),需要自己写换行符
- puts 不写终止符,自动添加换行符
- 输入 gets/fgets
- 每次一个字符
- 流的二进制 IO:解决字符 IO 场景遇到 null 停止的问题,fread/fwrite
- 不同机器或者不同编译程序和系统的区别导致,对象的成员变量偏移量不一样,从而无法正常工作,因此需要应用程序进行序列化和反序列化
- 定位流:ftell/fseek/ftello/feeko/fgetpos/fsetpos/rewind 修改读写文件流的位置
- 格式化 IO:printf/fprintf/dprintf/sprintf/snprintf
- 创建临时文件:
tmpnam
/tmpfile
/mkdtemp
/mkdstemp
- 重复调用会清楚静态区,因此文件名应该使用
char[] f = "foo"
而不是char *f = "foo"
- tmpfile 会先调用 tmpnam 生成一个唯一路径名,使用该路径名创建一个文件,然后立即 unlink 它,(存在时间窗口,不推荐使用)
- mkdtemp/mkstemp 创建的临时文件不会 unlink
- 重复调用会清楚静态区,因此文件名应该使用
- 内存流:没有底层文件,但是可以使用标准 IO 库读写
- fmemopen 创建内存流,可以指定内存流开始位置、大学、读写类型
- 任何时候需要增加流缓冲区中的数据量以及调用 fclose, fflush, fseek, fseeko, fsetpos 时都会在当前位置写入一个 null 字节
- 因为避免了内存溢出(缓冲区大小是固定的),内存流分非常适用于创建字符串