首页 > 代码库 > 读书笔记-APUE第三版-(5)标准IO库

读书笔记-APUE第三版-(5)标准IO库

ISO C标准I/O库使用流的概念读写文件。流是对数据传输的抽象,可以把流理解为从起点到终点间的字节序列。

标准I/O库通过维护进程空间内的缓冲区,减少read/write系统调用次数来提高I/O效率。之前介绍的Unbuffered I/O和文件描述符fd打交道,标准I/O则使用FILE指针。

typedef struct{
    short level;/*缓冲区满程度*/
    unsigned flags;/*文件打开状态标志*/
    char fd;/*文件描述符*/
    unsigned char hold;/*若无缓冲区不读取*/
    short bsize; /*缓冲区大小*/
    unsigned char *buffer; /*缓冲区位置*/
    unsigned ar *curp; /*当前读写位置*/
    unsigned istemp; /*临时文件指示*/
    short token; /*用作无效检测*/
}FILE;

缓冲区

在打开文件时,标准I/O库负责自动分配缓冲区,还可以通过setbuf/setvbuf函数显式设置缓冲区类型、大小甚至关闭缓冲区。

  1. Fully buffered(全缓冲):通常情况下,硬盘上的文件读写操作都使用全缓冲,当缓冲区满的时候才触发flush操作。
  2. Line buffered(行缓冲):行缓冲一般用于终端下的标准输入/输出操作,遇到换行符或者缓冲区满时都会发生I/O操作。(注:当标准I/O库不使用缓冲或者使用行缓冲需要从内核读取数据时,行输出缓冲会被flush)。
  3. Unbuffered(不缓冲):标准错误输出不使用缓冲。

打开/关闭文件

  1. 打开文件:FILE *fopen(const char *restrict pathname, const char *restricttype);打开类型如下:
  2. 三个预定义的文件流指针被自动打开,分别是stdin标准输入、stdout标准输出和stderr标准错误流。
  3. 关闭文件:int fclose(FILE *fp);所有缓冲输出数据被flush,缓冲输入数据被丢弃。进程正常退出时,所有文件流被自动关闭

读写操作

字符读写

#include"apue.h"
int main(void)
{
    int c;
    while ((c = getc(stdin)) != EOF)
        if (putc(c, stdout) == EOF)
            err_sys("output error");
    if (ferror(stdin))
        err_sys("input error");
    exit(0);
}

读取函数包括getc(通常为宏)/fgetc(不能是宏,只能是函数)/getchar(从标准输入流读取),它们都返回int整数,从unsignedchar转换而来,这样正常返回值都是正数,可以和EOF(典型值为-1)区分出来。

行读写

#include"apue.h"
int main(void)
{
    char buf[MAXLINE];
    while (fgets(buf, MAXLINE, stdin) != NULL)
        if (fputs(buf, stdout) == EOF)
            err_sys("output error");
    if (ferror(stdin))
        err_sys("input error");
    exit(0);
}

使用fgets/gets/fputs/puts要注意以下问题:缓冲区溢出,是否自动添加\0和\n等。

二进制读写

fread和fwrite函数用于读写二进制数据。要注意的是,由于字节对齐、字节顺序、编译器、计算机体系结构差异,二进制读写兼容性比较差。

struct {
    short count;
    long total;
    char name[NAMESIZE];
} item;
if(fwrite(&item, sizeof(item), 1, fp) != 1)
    err_sys("fwrite error");

效率对比


  1. 标准I/O的User CPU时间较高,因为相比第一行read/write操作BufferSize使用4096 ,标准I/O的while循环次数要多得多。
  2. 标准I/O的System CPU时间接近最优化状态,可见标准I/O会使用合适的缓冲区大小。
  3. 最后一行每读写一个字节都进行系统调用,所以性能最差

其他重要函数

  1. 文件流定位:ftell(返回long)/ftello(返回off_t)/fseek/fseeko/rewind/fgetpos/fsetpos,后两个是ISO C标准中定义的,可以在非UNIX系统中使用
  2. 格式化:printf家族:printf(标准输出)/fprintf(输出到文件)/sprintf(输出到字符数组缓冲区)/snprintf(指定缓冲区大小避免溢出)。相对应的是scanf家族。
  3. 临时文件:tmpnam(返回唯一路径)/tmpfile(创建临时文件,关闭后被自动删除)/mkdtemp/mkstemp(能指定临时文件名模板)。
  4. 内存流:fmemopen,把某段内存当做FILE流返回,然后进行I/O操作,一般用于创建字符串