使用 setvbuf 写入文件,有条件地丢弃缓冲区内容

问题描述

我想写一个简单的API

  1. 允许用户打开文件
  2. 用户将数据写入文件
  3. 跟踪写入调用并在每次写入调用后检查写入的数据。
  4. 防止无效的数据写入磁盘 -> discard(file)

作为起点,我编写了下面的测试程序,它使用 fopensetvbuf 在完全缓冲的“rb+”模式下打开一个文件。 由于以下原因,流以全缓冲模式打开:

http://www.cplusplus.com/reference/cstdio/setvbuf/

模式 指定文件缓冲模式。

三个特殊的宏常量[...]:

_IOFBF 全缓冲:在输出时,一旦缓冲区已满(或刷新)就会写入数据。在输入时,缓冲区在输入时填充 请求操作并且缓冲区为空。

我的测试程序包含可以放置有效性检查的注释以及应该丢弃缓冲区内容的注释。

我的问题是如何完成 discard(file) 操作,这意味着删除无效缓冲区内容的步骤?

这背后的想法是在缓冲区中组装一些数据,在每次或几次写入操作后进行定期有效性检查,如果数据有效,则仅将数据写入磁盘。 因此,如果有效性检查失败,我将需要丢弃缓冲区。 当有效性检查通过时,应该将整个缓冲区内容写入文件

我的代码草稿如下所示。这是一个简化的例子:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

int main(void)
{
    static uint8_t buffer[10000];
    
    /* The following would be part of mylib_init */
    FILE *file = fopen("test","wb+");
    
    if (file == NULL){
        print ("open error!");
        exit(-1);
    }
    
    if ( 0 != setvbuf(file,buffer,_IOFBF,sizeof(buffer) ) ){
        print("Could not set buffer!");
        fclose(file);
        exit (-2);
    }
    
    /* The following would be part of mylib_write_data.
       Each write and check resembles one func call */

    // Pretend the user writes some data into the file
    // ...
    // fwrite(x)
    
    if (data_in_buffer_not_valid(buffer)){
       discard(file);
    }

    // ...
    // fwrite(y)
    //

    if (data_in_buffer_not_valid(buffer)){
       discard(file);
    }

    // ...
    // fwrite(z)
    // ...
    
         
    // The following would be part of mylib_exit
    // Cleanup stuff
    fclose(file)

    return 0;
}

解决方法

如果您想要一些类似“scratch”的临时文件,您想将数据写入其中,然后在稍后检索它们,那么 可移植接口将是 tmpfile() - 它是一个专门为此创建的界面。写入该文件,rewind(如果需要),当您准备好时,rewind 并从它逐块读取到另一个文件。

在 linux 上,您可以使用 fmemopenfopencookie 通过 FILE* 写入缓冲区 - 这些函数在 Windows 上不可用。

我也强烈考虑创建自己的界面,将结果存储在内存中。编写 struct mystream; mystream_init(struct mystream *); mystream_printf(struct mystream *,const char *fmt,...); 等接口是您有时在 fopencookie 不可用时用 C 执行的一些任务。并考虑编写用于存储数据的接口,这样您就不会调用 fwrite,而是实际调用检查数据并写入数据并在此过程中对其进行处理的函数。

至于 setvbuf,请注意标准。来自C11 7.21.3p3

当流没有缓冲时,字符会尽快从源或目标出现。否则,字符可能会作为一个块累积并传输到主机环境或从主机环境传输。当流被完全缓冲时,[...]。当流被行缓冲时,[...] 对这些特性的支持是实现定义的,并且可能会受到 setbuf 和 setvbuf 函数的影响。

而且这些缓冲模式可能根本不支持。来自C11 7.21.5.6

setvbuf 函数只能在 stream 指向的流与打开的文件相关联之后,并且在对流执行任何其他操作(除了对 setvbuf 的不成功调用之外)之前使用。 [...] 数组的内容在任何时候都是不确定的

你不能指望缓冲区的内容是什么。不要指望那里有任何数据。