我想打开一个文件进行写入,以便在第一个写入指令时擦除并覆盖该文件。也就是说,文件在打开时应该保留其内容,并且只有在第一次插入时才会被实际覆盖。
使用std::fstream,如果我使用该模式std::ios_base::out,一旦调用构造函数,文件就会立即被擦除
#include <fstream>
int main()
{
std::fstream f("test.txt", std::ios_base::out);
// Now test.txt is empty
f << "hello";
}
如果 test.txt 不存在,这种行为是可以的。但是如果 test.txt 存在,我想保留它的内容,直到我写它。如果我打开它
std::fstream f("test.txt", std::ios_base::out | std::ios_base::in);
确实,现有文件仍被保留,并且仅在f << "hello". 但是,如果文件存在并且包含更多数据然后字符串“hello”,则f << "hello"只会覆盖前 5 个字符。我希望文件被擦除,就好像它是用 . 打开的一样std::ios_base::out,但只有在我执行f << "hello".
语境
像这样的例程会出现问题:
void do_something(std::ostream& s)
{
//... very long computation
s << "some infos" << std::endl;
}
我do_something称将流传递给它,它可能是也可能不是文件。如果它是一个文件,我希望能够在计算完成之前检查 test.txt。
一种可能性可能是将 a 包装在一个新对象中,该对象仅在被调用fstream时才实际打开文件,例如operator<<
#include <fstream>
class myfstream {
std::fstream m_f;
std::string m_filename;
public:
myfstream(std::string& filename) : m_filename{filename} { }
template <typename U>
myfstream& operator<<(const U& s) { if (!m_f.is_open()) m_f.open(m_filename, std::ios_base::out); m_f << s; return *this; }
};
该解决方案的缺点是缺乏灵活性:如果例程执行的do_something操作不仅仅是调用operator<<,则必须定义 的所有其他成员std::ostream,以模仿其行为。此外,为了保持灵活性do_something,必须声明为
template <typename S>
void do_something(S& o);
有更好的解决方案吗?例如,是否有可能在输出指标的当前位置之后fstream删除所有数据?
更新
tellp()显然,一个可能的解决方案是在关闭文件后获取实际的写入位置,然后调整它的大小。请参阅以下代码:
#include <fstream>
#include <filesystem>
#include <iostream>
int main()
{
typename std::fstream::pos_type size;
{
char d;
std::fstream f("test.txt", std::ios_base::in | std::ios_base::out);
std::cin >> d; // Just to pause
f << "hello";
size = f.tellp();
}
char d;
std::cin >> d; // Just to pause
std::filesystem::resize_file("test.txt", size);
return 0;
}
(std::cin那里只是暂停并能够检查文件的实际内容)。测试程序,在第一次“暂停”后,文件仍然完好无损。在第二次暂停之后,第一个字符已被字符串“hello”覆盖,但其后的任何文本仍保持不变。最后,resize_file将文件缩小到写入字符串的实际长度。
pos_type上面的代码在返回的tellp()astd::uintmax_t和所需的 from之间进行了隐式转换resize_file。
这种转换安全吗?