使用RAII写入文件结束标记
Use RAII for writing end of file marker?
我正在创建一种文件格式,我想在其中向文件中写入一条明确的消息,指示编写器已运行完成。我过去在生成文件时遇到过问题,生成程序崩溃,文件在我没有意识到的情况下被截断,因为如果没有明确的标记,读取程序就无法检测到文件不完整。
所以我有一个用来写这些文件的类。现在,如果你有一个"打开"操作和一个"关闭"操作,你想使用RAII,所以我会把代码写在析构函数中的文件结束标记。这样用户就不会忘记。但是,在由于抛出异常而导致写入未完成的情况下,析构函数仍将运行——在这种情况下,我们不想写入消息,这样读者就会知道文件不完整。
这似乎是任何时候都可能发生的"提交"操作。您想要RAII,这样您就不会忘记提交,但当发生异常时,您也不想提交。这里的诱惑是使用std::uncaught_exceptions,但我认为这是一种代码气味。
通常的解决方案是什么?只是要求人们记住吗?我担心每次有人试图使用我的API时,这都会成为一个绊脚石。
解决这个问题的一种方法是实现一个简单的框架系统,在该系统中,您可以定义一个只有在写入结束时才能完全填充的头。包括一个SHA256散列,使标头对验证文件内容有用。这通常比必须读取文件末尾的字节要方便得多。
在实现方面,您编写一个故意将某些字段置零的标头,在通过哈希方法提供数据的同时编写有效负载的内容,然后查找回标头并用最终值重写它。该文件一开始处于明显无效的状态,如果所有操作都已完成,则最终仅有效。
您可以将所有这些封装在一个流句柄中,该句柄处理实现细节,因此就调用代码而言,它只是打开一个常规文件。如果标头不完整或无效,您的读取版本将引发异常。
对于您的示例,如果添加一个commit
方法,RAII似乎可以正常工作,该方法是类的用户在完成对文件的写入时调用的。
class MyFileFormat {
public:
MyFileFormat() : committed_(false) {}
~MyFileFormat() {
if (committed_) {
// write the completion footer (I hope this doesn't throw!)
}
// close the underlying stream...
}
bool open(const std::string& path) {
committed_ = false;
// open the underlying stream...
}
bool commit() {
committed_ = true;
}
};
完成后,用户有责任调用commit
,但至少可以确保资源已关闭。
有关此类情况的更通用模式,请查看ScopeGuards。
ScopeGuards会将清理的责任从类中移出,并且可以用于在ScopeGuard超出范围并在被明确解除之前被销毁的情况下指定任意的"清理"回调。在您的情况下,您可以扩展这个想法,以支持失败清理(例如关闭文件句柄)和成功清理(例如写入完成页脚和关闭文件句柄的回调)。
我通过写入临时文件处理过类似的情况。即使要附加到文件,也要附加到该文件的临时副本。
在析构函数中,您可以检查std::uncaught_exception()来决定是否应该将临时文件移动到其预期位置。
- 文件追加的方式是,它在每次保存C++后结束行
- 读取二进制文件直到结束 c++
- 如何使用 AWS C++ 开发工具包在给定的开始和结束日期范围内列出 S3 中的文件
- 通过终端在文件中输入时检测EOF(文件结束)时出现问题
- 设置从文件中读取的开始和结束限制
- 带有文件结束函数的 while 循环重复输出文件中的最后一个数字两次
- C :使用文件末端(CTRL Z)结束一个循环似乎打破了我程序的其余部分
- 触发文件结束的原因
- 当存在空单元格时,用于 c++ 的 Tsv 文件解析器会突然结束
- 如何在 Visual Studio 2015 中的构建结束时复制文件
- 如何从"开始组..结束组"链接器选项构建文件列表
- 从 ifstream 读取,直到文件结束
- 输入文件读取错误的值,eof 控制循环不会结束
- C++:读取.BMP文件时出现问题;文件结束时间早于预期
- C 功能:读取直到文件结束 - 查找代码中的错误
- C :在文件结束时GetLine冻结
- 在崇高文本3上构建C 文件未结束
- 表示文件结束
- 提升asio read_u直到文件的异常结束
- 如何让程序知道当我使用 while(cin>> 时文件结束