APUE读书笔记(三)
5 标准I/O库
流和FILE对象
流的定向决定了读写字符是单字节(字节定向)还是多字节(宽定向)。
设置流的定向的模式(不改变已定向流):
#include <stdio.h> #include <wchar.h> int fwide(FILE *fp, int mode); //返回值:流为宽定向则返回正值,字节定向则返回负值,未定向则返回0 /* mode为负则指定字节定向; mode为正则指定宽定向; mode为0则不作设置而返回当前流定向状态 */
标准输入、标准输出和标准错误
标准输入、标准输出和标准错误的FILE流指针分别为stdin
、stdout
和stderr
。
缓冲
标准I/O提供:
- 全缓冲;
- 行缓冲;
- 不带缓冲。
冲洗是缓冲区的写操作。
不指向交互式设备的标准输入和标准输出是全缓冲(反之是行缓冲),标准错误不带缓冲。
更改缓冲类型:
#include <stdio.h> void setbuf(FILE *restrict fp, char *restrict buf);//buf为NULL则不带缓冲(否则缓冲区长度为BUFSIZE) int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);//mode为_IOFBF或_IOLBF且buf为NULL则选择合适长度系统缓冲区,为_IONBF且buf为NULL则无缓冲 //返回值:成功则返回0,出错则返回非0 /* mode: _IOFBF为全缓冲; _IOLBF为行缓冲; _IONBF为不带缓冲 */
强制冲洗一个流(使所有未写的数据被传送至内核):
#include <stdio.h> int fflush(FILE *fp); //返回值:成功则返回0,出错则返回EOF
打开流
打开标准I/O流:
#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);//将打开流指定为fp(已定向则先清除) FILE *fdopen(int fd, const char *type); //返回值:成功则返回文件指针,出错则返回NULL /* type: "r"或"rb"即读打开; "w"或"wb"即把文件截断至0或写打开; "a"或"ab"即追加(在文件尾写打开或写创建); "r+"或"r+b"或"rb+"即读写打开; "w+"或"w+b"或"wb+"即把文件截断至0或读写打开; "a+"或"a+b"或"ab+"即在文件尾读写打开或读写创建 */
关闭打开的流(关闭前冲洗缓冲区):
#include <stdio.h> int fclose(FILE *fp); //返回值:成功则返回0,出错则返回EOF
进程正常终止时会关闭所有打开的标准I/O流(先会冲洗缓冲区)。
读和写流
打开流后3种非格式化I/O:
- 每次一字符的I/O;
- 每次一行的I/O;
- 直接I/O。
一次读一字符:
#include <stdio.h> int getc(FILE *fp);//可被实现为宏 int fgetc(FILE *fp); int getchar(void);//等同于getc(stdin) //返回值:若成功则返回下一字符,若已到达文件尾或出错则返回EOF
流的出错相关操作:
#include <stdio.h> int ferror(FILE *fp);//检测出错 int feof(FILE *fp);//检测文件结束 //返回值:条件为真则返回非0,否则返回0 void clearerr(FILE *fp);//清除出错和文件结束标志
将字符压到流中:
#include <stdio.h> int ungetc(int c, FILE *fp); //返回值:成功则返回c,出错则返回EOF
一次写一字符:
#include <stdio.h> int putc(int c, FILE *fp);//可被实现为宏 int fputc(int c, FILE *fp); int putchar(int c);//等同于putc(c, stdout) //返回值:成功则返回c,出错则返回EOF
每次一行I/O
每次读一行:
#include <stdio.h> char *fgets(char *restrict buf, int n, FILE *restrict fp);//读到下一个换行符为止(读进去),不超过n-1个字符,缓冲区以null字节结尾 char *gets(char *buf);//与stdin相连,避免使用(不安全,缓冲区可能溢出) //返回值:成功则返回buf,已达到文件尾或出错则返回NULL
每次写一行:
#include <stdio.h> int fputs(const char *restrict str, FILE *restrict fp);//以null字节分割字符串但不将其写出(与换行符无关) int puts(const char *str);//与stdout相连,不推荐使用(不是不安全,尾部会加个换行符) //返回值:成功则非负值,出错则返回EOF
标准I/O效率
标准I/O库与直接调用read
和write
相比不慢很多。
二进制I/O
二进制读写操作:
#include <stdio.h> size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp); size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp); //返回值:读或写的对象数
二进制I/O可移植性较差。
定位流
三种方法定位标准I/O流:
- 假定文件的位置可以存放在长整型中:
#include <stdio.h> long ftell(FILE *fp);//得到当前位置 //返回值:成功则返回当前文件位置指示,出错则返回-1L
int fseek(FILE *fp, long offset, int whence);//设置位置 //返回值:成功则返回0,出错则返回-1
void rewind(FILE *fp);//回绕
- 使用
off_t
:#include <stdio.h> off_t ftello(FILE *fp);//得到当前位置 //返回值:成功则返回当前文件位置,出错则返回(off_t)-1
int fseeko(FILE *fp, off_t offset, int whence);//设置位置 //返回值:成功则返回0,出错则返回-1
- 由ISO C引入(可移植性好):
#include <stdio.h> int fgetpos(FILE *restrict fp, fpos_t *restrict pos);//得到当前位置 int fsetpos(FILE *fp, const fpos_t *pos);//设置位置 //返回值:成功则返回0,出错则返回
格式化I/O
- 格式化写:
#include <stdio.h> int printf(const char *restrict format, ...);//与stdout相连 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, ...);//不安全(缓冲区可能溢出) //返回值:成功则返回存入数组的字符数,编码出错则返回负值
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...); //返回值:缓冲区足够大则返回将要存入数组的字符数,编码出错则返回负值
使用arg
的版本:#include <stdarg.h> #include <stdio.h> int vprintf(const char *restrict format, va_list arg);//与stdout相连 int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg); int vdprintf(int fd, const char *restrict format, va_list arg); //返回值:成功则返回输出字符数,输出错误则返回负值
int vsprintf(char *restrict buf, const char *restrict format, va_list arg);//不安全(缓冲区可能溢出) //返回值:成功则返回存入数组的字符数,编码出错则返回负值
int vsnprintf(char *restrict buf, size_t n, const char *restrict format, va_list arg); //返回值:缓冲区足够大则返回将要存入数组的字符数,编码出错则返回负值
- 格式化读:
#include <stdio.h> int scanf(const char *restrict format, ...);//与stdin相连 int fscanf(FILE *restrict fp, const char *restrict format, ...); int sscanf(const char *restrict buf, const char *restrict format, ...); //返回值:赋值的输入项数,输入出错或任一转换前已到达文件尾则返回EOF
使用arg
的版本:#include <stdarg.h> #include <stdio.h> int vscanf(const char *restrict format, va_list arg);//与stdin相连 int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg); int vsscanf(const char *restrict buf, const char *restrict format, va_list arg); //返回值:指定的输入项数,输入出错或任一转换前已到达文件尾则返回EOF
函数fileno
将流指针转换为文件描述符:
#include <stdio.h> int fileno(FILE *fp); //返回值:与该流相关联的文件描述符
临时文件
ISO C提供的关于临时文件的函数:
#include <stdio.h> char *tmpnam(char *ptr);//仅生成文件名并存放在ptr中,ptr为NULL则其存在静态区(会复用)中 //返回值:指向唯一路径名的指针 FILE *tmpfile(void);//生成文件名并创建它,其为二进制文件(wb+),关闭文件或程序结束时自动删除(先unlink) //返回值:成功则返回文件指针,出错则返回NULL
SUS也定义了关于临时文件的函数:
#include <stdlib.h> char *mkdtemp(char *template);//创建目录(有用户读写执行权限,不自动删除) //返回值:成功则返回指向目录名的指针,出错则返回NULL int mkstemp(char *template);//创建文件(不自动删除) //返回值:成功则返回文件描述符,出错则返回-1
内存流
创建内存流:
#include <stdio.h> FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);//流关闭时缓冲区释放,会自动添加null字节 //返回值:成功则返回流指针,错误则返回NULL
其他创建内存流的两个函数:
#include <stdio.h> FILE *open_memstream(char **bufp, size_t *sizep); #include <wchar.h> FILE *open_wmemstream(wchar_t **bufp, size_t *sizep); //返回值:成功则返回流指针,出错则返回NULL //只能写打开,无法指定自己的缓冲区(但可以访问其地址和大小),流关闭时缓冲区释放,可以对缓冲区扩容