如何在 C++ 中减小 fstream 文件的大小

How to reduce the size of a fstream file in C++

在 C++ 11 中切断 fstream 文件结尾的最佳方法是什么

我正在编写数据持久性 class 来为我的音频编辑器存储音频。我选择使用 fstream(可能是个坏主意)来创建随机访问二进制读写文件。

每次我在我的文件中录制一点声音时,我只是将它附加到这个文件的末尾。另一个内部数据结构/文件,包含指向音频文件的指针并跟踪编辑。
当我撤消录制操作然后执行其他操作时,音频文件的最后一位变得无关紧要。它未在文档的当前状态中引用,您无法将自己恢复到可以再次看到它的状态。所以我想切掉文件的这一部分并在新的一端开始录制。我不需要在中间切掉比特,就在最后。

当用户退出时,该文件将保留并在他们再次打开项目时重新加载。

在我的应用程序中,我希望用户一直这样做并且能够这样做可能会为我节省多达 30% 的文件大小。这个文件会很长,可能非常非常长,所以每次发生这种情况时都将它重写到另一个文件不是一个可行的选择。

当用户保存时重写它可能是一种选择,但它仍然没有那么吸引人。

我可以在开头贴上一个值,说明文件应该有多长,然后覆盖结尾以回收 space 但与此同时。如果我想在崩溃的情况下不断更新数据存储文件,这意味着我将一遍又一遍地重写开始。我担心这可能对闪存驱动器不利。我还可以通过分析指针文件重新计算加载文件有用部分的结尾,但与此同时我可能会浪费所有 space,这很复杂。

在 fstream API 中是否有对此的简单调用?

我是不是用错了库?请注意,我想坚持使用我喜欢的通用 STL,这样我就可以使代码尽可能跨平台。

我好像在文档里找不到,找了好几个小时。这不是地球的尽头,但会让这更简单一点,并可能更有效。也许我只是以某种方式想念它。

感谢您的帮助 安德烈

如果您有变量,其中包含您在文件中的当前位置,您可以向后查找 "unnedeed chunk" 的长度,然后从那里继续写入。

// Somewhere in the begining of your code:
std::ofstream *file = new std::ofstream();
file->open("/home/user/my-audio/my-file.dat");
// ...... long story of writing data .......

// Lets say, we are on a one millin byte now (in the file)
int current_file_pos = 1000000;
// Your last chunk size:
int last_chunk_size = 12345;
// Your chunk, that you are saving
char *last_chunk = get_audio_chunk_to_save();
// Writing chunk
file->write(last_chunk, last_chunk_size);
// Moving pointer:
current_file_pos += last_chunk_size;
// Lets undo it now!
current_file_pos -= last_chunk_size;
file->seekp(current_file_pos);
// Now you can write new chunks from the place, where you were before writing and unding the last one!
// .....
// When you want to finally write file to disk, you just close it
file->close();
// And when, truncate it to the size of current_file_pos
truncate("/home/user/my-audio/my-file.dat", current_file_pos);

不幸的是,您必须编写一个跨平台函数 truncate,它会在 linux 中调用 SetEndOfFile in windows, and truncate。使用预处理器宏很容易。

Is there a simple call for this in the fstream API?

如果您有 C++17 编译器,则使用 std::filesystem::resize_file。在以前的标准中,标准库中没有这样的东西。

对于较旧的编译器...在 Windows 上,您可以使用 SetFilePointer or SetFilePointerEx to set the current position to the size you want, then call SetEndOfFile. On Unixes you can use truncate or ftruncate. If you want portable code then you can use Boost.Filesystem。从以后迁移到std::filesystem是最简单的,因为std::filesystem大多是基于它指定的。