• 关于标准IO缓冲区的问题


    关于标准IO缓冲区的问题

    按照标准IO缓冲区可以分为三类:

    不缓存类型:

    • 一旦有数据,直接将数据写入到文件

    行缓冲类型:

    • 同全缓冲类型
    • 遇到\n时,将数据写入文件

    全缓冲类型:

    • 当程序结束,将数据冲洗到文件
    • 当遇见fflush(),将数据冲洗到文件
    • 当文件关闭时,将数据冲洗到文件
    • 当遇到读取操作,将数据冲洗到文件
    • 当改变缓冲区的类型时,将数据冲洗到文件
    • 当缓冲区满了,将数据冲洗到文件

    对于标准输出而言,默认是行缓冲
    对于标准出错而言,默认是不缓存
    对于普通文件而言,默认是全缓冲

    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        // w+ 以读写的方式打开文件,若文件存在则清空文件数据,若文件不存在,则创建文件
        FILE *fp = fopen("_a.txt", "w+");
    
        // 将数据写入文件
        fputs("123", fp);
        
        //暂停程序不让,不让程序正常结束,观察a.txt文件内容
        pause();
        return 0;
    }
    

    我们能看到文件被创建,但是文件内没有内容。
    我们来分析一下,我们此时是以普通文件操作,此时是默认的全缓冲,且调用了pause()函数,程序没有正常的结束,所以能猜想到此刻的内容是在缓冲区里,没有被冲洗至文件中。如果我们把pause()进行注释,在执行该程序,我们能看到我们要写入的数据出现在_a.txt文件中。这也是全缓冲类型的第一次情况,当程序结束,将数据读取到文件。

    接下来我们来看一下这段代码:

    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("_a.txt", "w+");
    
        fputs("123", fp);
        fflush(fp);
    
        pause();
        return 0;
    }
    

    印证第二种情况我们直接用冲洗函数fflush(),能看到数据成功读取到文件中。

    第三种情况,在fput函数和pause函数中间,调用fclose(),关闭文件。

    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("_a.txt", "w+");
    
        fputs("123", fp);
        fclose(fp);
    
        pause();
        return 0;
    }
    

    第四种情况,当遇到读取操作,将数据冲洗到文件。

    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("_a.txt", "w+");
    
        fputs("123", fp);
        fgetc(fp);
    
        pause();
        return 0;
    }
    

    接下来我们看一下通过setvbuf()改变缓冲区的类型,进而对缓冲区的冲洗操作。

    简单的介绍一下:setvbuf函数

    image
    这图片是我在man手册截图下来的。

    int setvbuf(FILE *stream, char *buffer, int mode, size_t size)
    stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
    buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
    mode -- 这指定了文件缓冲的模式。

    _IOFBF 全缓冲
    _IOLBF 行缓冲
    _IONBF 无缓冲

    size --这是缓冲的大小,以字节为单位。

    返回值:如果成功,则该函数返回 0,否则返回非零值。

    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("_a.txt", "w+");
     // setvbuf(fp, NULL, _IONBF, 0);
        fputs("123", fp);
    
        setvbuf(fp, NULL, _IONBF, 0);
    
        pause();
        return 0;
    }
    
    

    我们这里的语句是setvbuf(fp, NULL, _IONBF, 0),把普通文件默认的全缓冲,转换成无缓冲,数据在文件中有所显示,所以符合我们的第五条理论。这是为了突出我们的第五条理论,因此将该语句放置在fput语句的后面,当然如果我们在setvbuf(fp, NULL, _IONBF, 0)后面写上相同的fputs语句或者将setvbuf(fp, NULL, _IONBF, 0),放置至fopen语句后面也是会相同的效果。不过第二种情况的理解不同,此刻能成功读取数据不是因为缓冲区类型的转换,而是因为它此刻已经被定义为无缓冲,所以数据直接读取至文件中。

    我们这里不妨试一试自己定义一个100字节的buf缓冲区,且在读取数据之前就将缓冲区类型转换至行缓存,观察是否能通过\n来将数据读取至文件当中去。(冲洗行缓冲)。

    
    #include 
    #include 
    int main(int argc, char const *argv[])
    {
        FILE *fp = fopen("_a.txt", "w+");
        char buf[1024];
        
        setvbuf(fp, buf, _IOLBF, 1024);
    
        //这里是行缓冲,如果要将数据冲洗至普通文件,注意要加\n
        fputs("123\n", fp);
        
        pause();
        return 0;
    }
    

    总之,滞留在缓冲区中的数据有时被称为脏数据(dirty data),脏数据的存在代表程序操作的结果与文件真实状态不一致,若未正常冲洗这些脏数据就退出程序则有可能会造成数据丢失,我们在使用IO对数据操作时要注意对缓冲区的问题

  • 相关阅读:
    拼多多官方开放平台接口app商品详情接口获取实时商品详情数据演示
    Redis 中常见的集群部署方案
    使用canal解决Mysql和Redis数据同步(TCP)
    《算法竞赛进阶指南》靶形数独
    多数互联网人对2021年终奖不抱期待
    [附源码]Python计算机毕业设计Django项目管理系统的专家评审模块
    Nginx + tomcat 搭建
    Vue3的Teleport:Teleport是Vue3的一个新功能,它允许我们将子组件渲染到父组件以外的地方,这在处理模态框、弹出窗口等情况时非常有用
    c++ double free错误解决
    mysql连接查询
  • 原文地址:https://www.cnblogs.com/yuniyuyuan577/p/16528454.html