简体   繁体   English

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

[英]write text file with simultaneous random access and append

The following piece of code tries to overwrite to different locations in a file and also to append to the file. 下面的代码尝试覆盖文件中的不同位置,并附加到文件中。 However, it does not seem to work. 但是,它似乎不起作用。 I have tried several modes and the result is either overwriting or simply appending everything to the end of the file but not both together. 我已经尝试了几种模式,结果是覆盖或只是将所有内容附加到文件的末尾,但不是两者都在一起。 Is it possible to do such a thing in C++? 是否有可能在C ++中做这样的事情? Or, is it that we need to open the file again and again in different modes to accomplish this? 或者,是否需要在不同模式下反复打开文件才能实现此目的?

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;
}

Are you looking to insert data somewhere in the file that is not the end? 您是否希望在文件中的某个位置插入数据? There is no insertion, at least with fstream s, in my experience. 根据我的经验,至少在fstream没有插入。 This is much like writing data in an array. 这很像在数组中写入数据。 Simply having a pointer somewhere between the beginning and end of the array and writing there doesn't insert, it overwrites. 只需在数组的开头和结尾之间的某处指定并在那里写入就不会插入,它会覆盖。

Handle it like you would inserting data into the middle of an array. 处理它就像将数据插入数组的中间一样。 You have to move data forward to make space for the insertion. 您必须向前移动数据以为插入腾出空间。

Calculate how much space you'll need for the insertion and read all data after the insertion point to a buffer, the write the data in that buffer back into the file after you've moved your write pointer forward by the amount of space you'll need for the insertion. 计算插入所需的空间大小,并在插入缓冲区后读取所有数据,在将写入指针向前移动一段空间后,将该缓冲区中的数据写回文件中我需要插入。 Then, move your write pointer back to where you wanted to insert and write the data you plan to insert. 然后,将写指针移回您想要插入的位置并写入您计划插入的数据。

  1. Calculate insertion data length. 计算插入数据长度。
  2. Seek to insertion point. 寻求插入点。
  3. Read rest of file to buffer. 读取文件的其余部分以缓冲。
  4. Seek to insertion point. 寻求插入点。
  5. Write insertion data. 写入插入数据。
  6. Write from rest of file buffer. 从文件缓冲区的其余部分写入。

EDIT: The requested code example: 编辑:请求的代码示例:

#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();
}

A few notes. 几点说明。 The earlier in the file that you insert, the more expensive it is because you need to move everything after it. 您插入的文件越早,它就越贵,因为您需要在它之后移动所有内容。 That also means bigger files are more expensive to insert into. 这也意味着更大的文件插入更昂贵。 If you're going to do several inserts, you can try to keep track of each insertion and defer them until some kind of flush command is called, at which point only 1 major read/write of the rest of the file is necessary, instead of one for each insertion call. 如果你要进行多次插入,你可以尝试跟踪每个插入并推迟它们,直到调用某种flush命令,此时只需要对文件的其余部分进行1次主要的读/写操作,而不是每个插入调用一个。 Ideally, you should wrap this up in your own wrapper of some sort. 理想情况下,您应该将它包装在您自己的某种包装中。

If you specify ios::app when you open the file all writes will always go to the end of the file -- it's basically as if every write was preceded by f.seekp(ios::end) . 如果在打开文件时指定ios::app ,则所有写入将始终到达文件的末尾 - 这基本上就好像每次写入都在f.seekp(ios::end)

It sounds like what you want is ios::ate instead. 这听起来像你想要的是ios::ate This will seek to the end of the file immediately after it's opened, but when/if you seek to somewhere else in the file and write, the write will go to the current point in the file instead of the end. 这将在文件打开后立即搜索到文件的末尾,但是当/如果您寻找文件中的其他位置并写入时,写入将转到文件中的当前点而不是结束。

Edit 3: I've added code to seek back to the end and append more there: 编辑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;
}

Starting with the following as the content of the file "test.txt": 从以下内容开始,作为文件“test.txt”的内容:

Initial  Value.

After running the program, the file should contain: 运行程序后,该文件应包含:

Original value. added to end. Final.

So, "Added to end." 所以,“添加到结束。” gets, obviously enough, added to the end. 很明显,这已经到了最后。 We then seek back to the beginning, and overwrite 'Initial" (plus one of the two spaces after it) with "Original". 然后我们回到开头,用“原始”覆盖'Initial'(加上其后的两个空格中的一个)。

As an aside, I should add that I was apparently at least half asleep when I posted last night -- I completely missed the fact that you had only supplied on parameter when you called seekp . seekp ,我应该补充一点,当我昨晚发布时,我显然至少睡了一半 - 我完全错过了你在调用seekp时只提供了参数的seekp With only one parameter, it takes that as the offset into the file. 只有一个参数,它将作为文件的偏移量。 Unfortunately, std::ios::beg , std::ios::cur and std::ios::end are simple integer values, so instead of giving a type error (as you'd really like) it was simply taking the value of those enumerations, and using them as offsets into the file. 不幸的是, std::ios::begstd::ios::curstd::ios::end是简单的整数值,所以不是给出类型错误(正如你真正喜欢的那样),它只是采取了这些枚举的 ,并将它们用作文件的偏移量。 Purely by luck, std::ios::beg apparently has the value 0 (at least on my implementation) so it "worked", but only by accident. 纯粹靠运气, std::ios::beg显然具有值0(至少在我的实现上)所以它“工作”,但只是偶然。 With two parameters (as the code above has now) the first is the offset, and the second it the reference point (beginning, current position or end) from which that offset starts. 使用两个参数(如上面的代码所示),第一个是偏移量,第二个是偏移量开始的参考点(开始,当前位置或结束)。

What you'll need to know: 你需要知道的:

  • Append mode writes to the end of the file, no matter what seeking you may do 追加模式写入文件的末尾,无论您寻求什么样的搜索
  • Even in other writing modes, writing at the beginning of the file does not prepend, it overwrites. 即使在其他写入模式下,在文件开头写入也不会预先填写,它会被覆盖。 This is not the "fault" of C++ so much as it is the filesystem. 这不是C ++的“错误”,而是文件系统。 It's just the way file manipulation works, and will be the case for just about every platform and every language out there. 这就是文件操作的工作方式,几乎每个平台和每种语言的情况都是如此。
  • If you really want to insert text in other places, you'll probably need to read the file into memory, manipulate it, then re-write the file to disk. 如果你真的想在其他地方插入文本,你可能需要将文件读入内存,操作它,然后将文件重新写入磁盘。 Or in some cases it might make sense to use something like sed to manipulate the file. 或者在某些情况下,使用像sed这样的东西操作文件可能是有意义的。

Languages that provide file preprend capabilities out of the box are rare. 开箱即用提供文件预发布功能的语言很少见。 I don't know of any that do, unless you count special purpose languages like sed that aren't good for anything but file manipulation. 我不知道有什么事,除非你算上特殊用途的语言,如sed那些不好的东西,但文件操作。 There may be languages that do what you want in the way you were hoping, but I don't know of any. 可能有一些语言按照你希望的方式做你想做的事情,但我不知道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM