《Unix环境高级编程》 总结 (三)

来源:互联网 发布:学汉语翻译软件 编辑:程序博客网 时间:2024/05/16 17:12

标准I/O库 (第五章)

1. 流定向

标准I/O可用于单字节或多字节。流最初创建时没有定向,第一次使用多字节I/O,则定向为宽字节;使用单字节则为字节定向。可改变定向的函数:freopen()和fwide(),如下:

#include <stdio.h>#include <wchar.h>int fwide(FILE *fp, int mode);     //                宽字节返回正;单字节返回负;未定向返回0    //mode<0:设为字节定向;    //mode>0:设为宽字节定向;    //mode=0:不设置,返回当前定向;

2. 标准输入、输出和出错

#include <stdio.h>stdinstdoutstderr

3. 缓冲

标准I/O的三种缓冲:
- 全缓冲 : 缓冲区填满或者调用fflush()时执行I/O;对于fflush(),若为标准I/O,则将数据写入磁盘;若为终端设备,则丢弃缓冲区中的数据;
- 行缓冲 : 遇到换行符时,执行I/O。如果缓冲区满了,即使没有换行符也要执行I/O;
- 不带缓冲,如stderr

ISO C缓冲特征
- 当且仅当标准输入、输出不指向交互式设备时,它们才是全缓冲;
- stderr决不是全缓冲

默认情况下:stderr不带缓冲;指向终端设备的流为行缓冲,其它为全缓冲。
更改缓冲的函数,如下:

#include <stdio.h>void setbuf(FILE *restrict fp, char *restrict buf);    //buf : 设为指向长度为 BUFSIZ 的缓冲区,则为带缓冲I/O;设为NULL则关闭缓冲int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);    //                     成功返回0,失败非0    //mode : _IOFBF(全缓冲);_IOLBF(行缓冲):_IONBF(无缓冲)    //若带缓冲,而buf为NULL,则自动分配缓冲(BUFSIZ)int fflush(FILE *fp);    //若fp为NULL,则冲洗所有输出流。

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);    //将指定文件打开为指定流,如stdin/stdout/stderrFILE *fdopen(int fd, const char *type);    //                    出错返回NULL    //常用于管道或Socket返回的fdint fclose(FILE *fp);    //冲洗缓冲中的输出数据,丢弃输入数据。

type参数如下:

image

b表示二进制,Linux中无用(Linux不区分文本文件和二进制文件);
fdopen以写打开不会截断文件;
追加写方式不能用于创建文件,所以pathname必须存在。

5. 读写流

  • 每次读写一个字符,如下:
#include <stdio.h>int getc(FILE *fp);    // 实现为宏int fgetc(FILE *fp);    // 实现为函数int getchar(void);    // 同 getc(stdin)    //              到达文件末尾或出错返回 EOFint putc(int c, FILE *fp);int fputc(int c, FILE *fp);int putchar(int c);    // 成功返回c,出错返回EOF

EOF为一负值,常为-1。要区分实际问题,用下面函数:

#include <stdio.h>int ferror(FILE *fp);int feof(FILE *fp);    //              条件为真返回非0;否则返回0

每个流在FILE中有两个标志:出错标志和文件结束标志,可用clearerr清除这两个标志。
将字符再压回流中,如下:

#include <stdio.h>int ungetc(int c, FILE *fp);    // 成功返回c,出错返回EOF

送回的字符可再读出,但顺序相反,送回的可为其它字符,但不能为EOF,到文件尾仍可再送回字符。该方法只是将字符写到了绥冲区中。
- 每次读写一行,如下:

#include <stdio.h>char *fgets(char *restrict buf, int n, FILE *restrict fp);    // 最多读取 n-1 个字符;保留换行char *gets(char *buf);    // 无换行    // 文件尾或出错返回NULLint fputs(const char *restrict str, FILE *restrict fp);    // 需要自己添加换行符int puts(const char *str);    // 自动添加换行符    // 成功返回非负值,出错返回EOF
  • 每次读写一个结构
#include <stdio.h>size_t fread(void *restrict ptr, size_t size, size_t nobj, File *restrict fp);    // 返回值少于nobj,则调用ferror()和feof()判断是出错还是文件尾size_t fwrite(void *restrict ptr, size_t size, size_t nobj, File *restrict fp);    // 返回值少于nobj则出错    // 返回读写对象数

6. 定位流

#include <stdio.h>long ftell(FILE *fp);    // 成功返回当前位置,出错返回 -1Lint fseek(FILE *fp, long offset, int whence);    // 成功返回0,出错返回 -1,文件函数用法同 lseek()    // 对于文本文件,whence一定为SEEK_SET,且offset只能为0或ftell()的返回值void rewind(FILE *fp);    // 将流设到起始位置off_t ftello(FILE *fp);    // 成功返回当前位置,出错返回 (off_t)-1int fseeko(FILE *fp, off_t offset, int whence);int fgetpos(FILE *restrict fp, fpos_t *restrict pos);int fsetpos(FILE *fp,const fpos_t *pos);    // 调用fgetpos()将位置存入pos,以后可调用fsetpos()重新定位到该位置。    // 成功返回0;出错返回非0

7. 格式化I/O

#include <stdio.h>int printf(const char *restrict format, ...);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, ...);    // 成功返回存入buf的字符数,出错返回负值(在buf中自动添加'\0'
#include <stdio.h>#include <stdarg.h>int vprintf(const char *restrict format, va_list arg);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);    // 成功返回存入buf的字符数,出错返回负值(在buf中自动添加'\0')
#include <stdio.h>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, ...);
#include <stdio.h>#include <stdarg.h>int vscanf(const char *restrict format, va_list arg);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);

8. fileno() 从FILE中得到fd

#include <stdio.h>int fileno(FILE *fp);

9. 临时文件

创建临时文件,如下:

#include <stdio.h>char *tmpnam(char *ptr);    // 返回唯一路径名的指针,最多调用次数(TMP_MAX)    // ptr为NULL,则产生的路径名在静态区中,后续再调用会覆盖;    // ptr不为NULL,则应指向长度至少为L_tmpnam(strio.h)的buf。FILE *tmpfile(void);    // 创建的二进制文件(wb+),在关闭文件或程序结束时自动删除。
#include <stdlib.h>char *mkdtemp(char *template);    // 成功返回目录名的指针,失败返回NULL    // 目录权限位:0700int mkstemp(char *template);    // 成功返回文件fd,失败返回 -1    // 以读写方式打开,不会自动删除

template是后6位为XXXXXX的路径名,如下:
“/tmp/dirXXXXXX” -> /tmp/dirUmBT7h
tmpnam()和tempnam()缺点:有时间差,另的进程可以用相同的名字创建文件,所以最好用 tmpfile()和mkstemp()

10. 内存流

#include <stdio.h>FILE* fmemopen(void *restrict buf, size_t size, const char *restrict type);FILE* open_memstream(char**ptr, size_t* sizeloc);#include <wchar.h>FILE* open_wmemstream(wchar_t** ptr, size_t* sizeloc);

image

如果buf为NULL,则fmemopen分配size字节数的缓冲区,此时当流关闭时缓冲区会被释放。
fmemopen()函数打开一个内存流,使你可以读取或写入由buf指定的缓冲区。其返回FILE*fp就是打开的内存流,虽然仍使用FILE指针进行访问,但其实并没有底层文件(并没有磁盘上的实际文件,因为打开的内存流fp是在内存中的),所有的I/O都是通过在缓冲区与主存(就是内存)之间来回传送字节来完成的。
重点内容
open_memstream()函数打开一个面向字节的流来写入一个缓冲区,而open_wmemstream()函数创建一个面向宽字符的流。当用fclose()关闭流或用fflush()刷新流时,bufp和sizep被更新,以包含缓冲区的指针及其大小。只要没有进一步的输出流发生,这些值仍然有效。如果执行额外的输出,必须再次刷新流来存储新的值,才能再次使用它们。一个空字符被写入缓冲区的末尾,但它存储在sizep中的size值中不包括它。

通过调用fmemopen()、open_memstream()或open_wmemstream()创建的一个与内存缓冲区相关联的流的输入和输出操作,发生在内存缓冲区的范围内,受限于实现。对于用open_memstream()或open_wmemstream()打开的流的情况,内存区域动态增长,以适应必要的写操作。对于输出,在刷新或关闭操作期间,数据从函数setvbuf()提供的缓冲区移动到内存流。如果没有足够的内存来增长内存区域,或者操作需要访问相关内存区域以外的地方,相关的操作失败。
因为避免了缓冲区溢出,内存流非常适用于创建字符串。因为内存流只访问主存,不访问磁盘上的文件,所以对于把标准I/O流作为参数用于临时文件的函数来说,会有很大的性能提升。

原创粉丝点击