写文本文件同时随机访问并追加

时间:2011-03-03 03:20:05

标签: c++ file-io

以下代码尝试覆盖文件中的不同位置并附加到文件中。但是,它似乎不起作用。我已经尝试了几种模式,结果是覆盖或只是将所有内容附加到文件的末尾,但不是两者都在一起。是否有可能在C ++中做这样的事情?或者,我们是否需要在不同模式下一次又一次地打开文件才能完成此任务?

int main() {
  fstream f("a.dat", ios::out | ios::app);
  f.seekp(ios::beg);
  f << "hello world";
  f.seekp(ios::end);
  f << "works";
  return 0;
}

3 个答案:

答案 0 :(得分:5)

您是否希望在文件中的某个位置插入数据?根据我的经验,至少有fstream s没有插入。这很像在数组中写入数据。只需在数组的开头和结尾之间的某处指向并在那里写入就不会插入,它会覆盖。

处理它就像将数据插入数组的中间一样。您必须向前移动数据以为插入腾出空间。

计算插入所需的空间,并在插入缓冲区后读取所有数据,在将写入指针向前移动之后,将该缓冲区中的数据写回文件中您需要插入空间。然后,将写指针移回您想要插入的位置,并写入您计划插入的数据。

  1. 计算插入数据长度。
  2. 寻求插入点。
  3. 将剩余文件读取到缓冲区。
  4. 寻求插入点。
  5. 写入插入数据。
  6. 从文件缓冲区的其余部分写入。
  7. 编辑: 请求的代码示例:

    #include <fstream>
    
    int main(int argc, char* argv[])
    {
        std::fstream FileStream;
    
        // Initial write.
        FileStream.open("Test.txt", std::ios::out | std::ios::trunc);
        FileStream << "OLDDATAOLDDATA";
        FileStream.close();
    
        // Prepare data to insert.
        const char InsertionData[] = "newdata";
        const unsigned int InsertionDataLength = strlen(InsertionData);
    
        // Insertion write.
        FileStream.open("Test.txt", std::ios::in | std::ios::out | std::ios::ate);
    
        std::ios::pos_type InsertionPosition = 7; // 7 is the start index of the second "OLDDATA".
        FileStream.seekg(0, std::ios::end);
        std::ios::pos_type EndPosition = FileStream.tellg();
    
        // Read rest of file.
        const unsigned int RestOfFileDataLength = EndPosition - InsertionPosition;
        char* const RestOfFileData = new char[RestOfFileDataLength];
        FileStream.seekg(InsertionPosition);
        FileStream.read(RestOfFileData, RestOfFileDataLength);
    
        // Rewrite rest of file.
        FileStream.seekp(InsertionPosition);
        FileStream.write(InsertionData, InsertionDataLength);
        FileStream.write(RestOfFileData, RestOfFileDataLength);
        delete[] RestOfFileData;
    
        FileStream.close();
    }
    

    一些笔记。您插入的文件越早,它就越贵,因为您需要在它之后移动所有内容。这也意味着更大的文件插入更昂贵。如果你要进行多次插入,你可以尝试跟踪每个插入并推迟它们,直到调用某种flush命令,此时只需要对文件的其余部分进行1次主要的读/写操作,而不是每个插入调用一个。理想情况下,您应该将它包装在您自己的某种包装中。

答案 1 :(得分:4)

如果您在打开文件时指定ios::app,则所有写入将始终到达文件末尾 - 基本上就好像每个写入前面都有f.seekp(ios::end)

听起来你想要的是ios::ate。这将在文件打开后立即寻找文件的末尾,但是当/如果你寻找文件中的其他地方并写入时,写入将转到文件中的当前点而不是结束。

编辑3:我添加了代码以回到最后并在那里添加更多内容:

#include <fstream>
#include <iostream>

int main() { 
    std::fstream f("test.txt", std::ios::in | std::ios::out | std::ios_base::ate);

     f << " added to end.";
     f.seekp(0, std::ios::beg);
     f << "Original";
     f.seekp(0, std::ios::end);
     f << " Final.";

     return 0;
}

从以下文件“test.txt”的内容开始:

Initial  Value.

运行程序后,该文件应包含:

Original value. added to end. Final.

所以,“添加到结束。”很明显,这已经到了最后。然后我们回到开头,用“原始”覆盖'Initial'(加上其后的两个空格中的一个)。

顺便说一句,我应该补充一点,我昨晚发布时显然至少睡了一半 - 我完全错过了你在调用seekp时只提供参数的事实。只有一个参数,它将作为文件的偏移量。不幸的是,std::ios::begstd::ios::curstd::ios::end是简单的整数值,所以不是给出类型错误(正如您真正喜欢的那样),而是仅仅取,并将它们用作文件的偏移量。仅凭运气,std::ios::beg显然具有值0(至少在我的实现中),所以它“有效”,但只是偶然。有两个参数(如上面的代码所示),第一个是偏移量,第二个是偏移量开始的参考点(开始,当前位置或结束)。

答案 2 :(得分:1)

您需要了解的内容:

  • 追加模式会写入文件末尾,无论您寻求什么样的搜索
  • 即使在其他写入模式下,在文件开头写入也不会预先填写,它会被覆盖。这不是C ++的“错误”,而是文件系统。这就是文件操作的工作方式,几乎每个平台和每种语言都是如此。
  • 如果你真的想在其他地方插入文本,你可能需要将文件读入内存,操作它,然后将文件重新写入磁盘。或者在某些情况下,使用sed之类的东西操作文件可能是有意义的。

提供开箱即用的文件预呈现功能的语言很少见。我不知道有什么用,除非你算上像sed这样的特殊目的语言,这些语言不适用于任何文件操作。可能有一些语言按照你希望的方式做你想做的事,但我不知道。