• 关于标准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对数据操作时要注意对缓冲区的问题

  • 相关阅读:
    并发编程之Executor线程池原理与源码解读
    【NLP屠夫系列】- NER之实战BILSTM
    PyTorch C++扩展用于AMD GPU
    【趣味随笔】盘点国内外做双足机器人的公司
    第十天:基于Ubuntu和gec6818开发板的QT图书管理系统完整项目设计
    【英语:基础进阶】D3.听力数字与时间类问题
    SpringBoot使用@Async注解8大坑点
    C++ list容器模拟实现
    分享一份接口测试项目(非常值得练手)
    30 个 Linux 监控的top命令示例
  • 原文地址:https://www.cnblogs.com/yuniyuyuan577/p/16528454.html