0

我想打开一个文件进行写入,以便在第一个写入指令时擦除并覆盖该文件。也就是说,文件在打开时应该保留其内容,并且只有在第一次插入时才会被实际覆盖。

使用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

这种转换安全吗?

4

0 回答 0