美文网首页
APUE标准i/o

APUE标准i/o

作者: m风满楼 | 来源:发表于2019-07-20 20:51 被阅读0次

    流和FILE对象

    文件IO的所有操作都是基于文件描述符fd,而标准IO的所有操作都是围绕流进行的。当用标准I/O库打开或创建一个文件时,我们使一个流和一个文件关联

    标准输入,标准输出,标准出错

    对每个进程预定于流三个流,他们是STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO

    缓冲

    标准I/O库提供缓冲对目的是尽可能对减少使用read/write系统调用的次数,它对每个I/O流自动地进行缓冲管理。
    标准I/O提供三种形式的流管理

    • 全缓冲
      在填满缓冲区后才进行I/O,可以手动调用fflush手动缓冲,
    • 行缓冲
      当I/O 中携带遇到换行符是,才进行I/O操作,这允许我们一次写入一个字符。当流涉及终端时,通常使用行缓冲。行缓冲的长度通常是固定的,当超过缓冲区长度后,也会进行I/O操作。任何时候只要通过标准I/O库要求从一个1)不带缓冲的流,或2)行缓冲的流得到输入数据,都将冲洗所有行缓冲输出流。
    • 不缓冲
      不对字符进行缓冲存储,标准错误通常不缓冲。
    更改流的缓冲类型
    #include<stdio.h>
    void setbuf(FILE *restrict fp, char *restrict buf);
    int  setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
        #成功返回0,出错返回非0值
        #关闭缓冲,将buf设置为NULL,
        #setvbuf,可以精确制定缓冲类型,使用mode=(_IOFBF,_IOLBF,_IONBF),如果不缓冲,忽略buf和size参数,如果制定行缓冲和全缓冲,需要制定相应的缓冲区以及长度
    int fflush(FILE *fp) #冲洗缓冲区
    
    打开流
      #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 fileds, const char *type)
    #获取一个现有文件描述符,,并是一个标准I/O流与该文件描述符结合.常用于由创建管道和网络通信通道函数返回的描述符。
        #成功返回文件指针,出错返回NULL
    int fclose(FILE *fp)
        #成功返回0,出错返回EOF 
    

    type通常指 r,w,a...
    当以读写类型(r+,w+)打开文件时,具有下列限制

    • 如果中间没有fflush,fseek,fsetpos,rewind,则在输出后不能直接跟随输入
    • 如果中间没有fseek,fsetpos或rewind,或者一个输入操作没有到达文件尾端,则在输入操作后不能直接跟随输出。

    在文件关闭之前,会冲洗缓冲区的所有数据,丢弃缓冲区中任何输入数据,如果标准I/O为该流自动分配了一个缓冲区,则释放此缓冲区。
    当一个进程正常正常终止时,则所有未写缓冲数据的标准I/O流都会被冲洗,所有打开的I/O流都会被关闭。

    读写一个字符

    #include<stdio.h>
      int getc(File *fp)
      int fgetc(File *fp)
      int getchar(void)  = getc(stdin)  #从标准输入读
    #若成功,则返回下一个字符,出错或达到文件尾端返回EOF
    #未区分出错或到达文件尾端,可以使用如下函数
      int ferror(FILE *fp)
      int feof(FILE *fp)
      #条件为真返回非0,否则返回0
      void clearerr(FILE *fp)
    

    每个流在FILE中维护两个标志

    • 出错标志
    • 文件结束标志
      调用clearerr可以清楚这两个标志。
      从流中读取数据后,可以使用ungetc将字符压回流中
    #include<stdio.h>
    int ungetc(int c, FILE *fp)
      #成功返回c,则出错返回EOF
      # 压送回的流可以被再次读出,但读出字符的顺序与压送回的顺序相反。
      #ungetc会清除该流的文件结束标记
    

    每次一行I/O

    #include<stdio.h>
    char fgets(char *restrict buf, int n, FILE *fp)
    #必须指定缓冲区的长度n,如果没有读到换行符,则读到n-1个字节为止。如果该行长度超过n-1,则fgets会返回不完整的行
    char *gets(char *buf) #从标准输入读 
    #gets不推荐使用,因为无法指定缓冲区长度,如果写入的内容大于缓冲区长度,会造成缓冲区溢出,写到缓冲区后面的空间中。
    #成功返回buf,出错到文件末尾返回NULL
    
    #include<stdio.h>
    int fputs(const char* restrict buf, FILE *restrict fp)
    #以null终止的字符串写入到指定的流,尾端终止符null不写出。
    int puts(const char *str)
    # 将以null符终止的字符串写到stdout,null终止符不写出,但是puts又将一个换行符写到stdout
    #成功返回非负值,失败返回EOF
    

    疑点:如何处理换行符
    尽量使用fgets,fputs

    标准I/O的效率

    二进制I/O

      #include<stdio.h>
    size_t fread(void *restrict ptt, 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)
      #返回读或写的对象数
    

    使用二进制io的时候,只能用于读在同一系统已写过的数据,无法对异构系统数据进行读写。

    定位流
    long ftell(FILE *fp)
      #返回值:若成功则返回当前未见位置指示,出错返回-1L
    int fseek(FILE *fp, long offset, int whence)
      # 返回,若成功返回0,出错返回非0
    void rewind(FILE *fp)
    
    格式化I/O
    #include<stdio.h>
    int printf(const char *restrict format,...);
    int fprintf(FILE *restrict fp, const char *restirct format ,...);
    #写到指定的流
      #成功返回输出字符数,出错返回负值
    int sprintf(char *restrict buf, const char *restrict format,...);
    #将格式化字符写送入数组buf中,该函数存在buf指向的缓冲区溢出的风险,调用者有责任确保该缓冲区足够大。
    int snprintf(char8restirct buf, size_t n, const char *restrict format,...)
    #相较于上面的sprintf,该函数指定了缓冲区长度,如果超过长度的数据,会被丢弃。
      #成功返回存入数组的字符数,出错返回负值
    

    总结:
    标准io通过对文件i/o的封装,来进行输入输出操作,在操作对象上,标准io操作的对象是流,文件io操作的是文件描述符。
    文件io每次read,write都调用内核中的系统调用,而标准io则通过缓冲区减少系统调用的次数。

    相关文章

      网友评论

          本文标题:APUE标准i/o

          本文链接:https://www.haomeiwen.com/subject/xvislctx.html