fstream seekg()、seekp()和write()的作用是什么?

26

我想要一些关于如何使用 seekg()seekp() 在写文件时的澄清。例如,假设我有一个这样的文件:

offset 0: 2
offset 4: 4
offset 8: 6
offset 12: 8
offset 16: 10

现在我想要打开文件并执行一些查找以读取和写入值。

fstream file;
file.open("file.txt", fstream::in |fstream::out | fstream::binary);
file.seekp(0, ios::end) // seek to the end of the file
int eofOffset = file.tellp(); // store the offset of the end-of-file, in this case 20

int key = 0;

file.seekg(12, ios::beg); // set the seek cursor to offset 12 from the beginning of the file
file.read((char *) &key, (int)sizeof(int)); // read in the next 4 bytes and assign it to key, in this case 8
file.seekg(8, ios::beg); // set the seek cursor to offset 8 from the beginning of the file
file.read((char *) &key, (int)sizeof(int)); // read in the next 4 bytes and assign it to key, in this case 6

现在我想要将内容写入文件末尾。由于 seekg() 函数只移动了读指针,我的 seekp() 指针应该仍然位于文件末尾,对吗?所以:

int newKey = 12;
file.write((char *) &newKey, sizeof(int));

现在应该让我的文件看起来像:

offset 0: 2
offset 4: 4
offset 8: 6
offset 12: 8
offset 16: 10
offset 20: 12

如果我选择将文件指针移动到某个偏移并将该值作为新插入值的偏移写入,我的文件会发生什么?例如,我希望偏移量 8保持eofOffset = 20,因为我们刚刚在该偏移处插入了12。

如果我这样做:

file.seekp(8, ios::beg);
file.write((char *) &eofOffset, sizeof(int));

它是否正确地重写了我的文件,使其看起来像这样:

offset 0: 2
offset 4: 4
offset 8: 20
offset 12: 8
offset 16: 10
offset 20: 12
请告诉我,如果我在使用 seekg()seekp() 函数时有任何错误,请指出。

你提供了太多无用的信息,只需要说seekg()seekp()移动相同的指针,可以通过tellg()tellp()来查看。 - Ben
1个回答

25

类模板std::basic_filebuf保存单个文件位置。

§ 27.9.1.1

  1. 基本文件流类将输入序列和输出序列与文件关联。
  2. 使用basic_filebuf对象控制的序列读写的限制与使用标准C库FILE进行读写的限制相同。
  3. 特别地:
    • 如果文件未打开以供读取,则无法读取输入序列。
    • 如果文件未打开以供写入,则无法写入输出序列。
    • 输入序列和输出序列都维护一个共同的文件位置。

这意味着当您使用std::basic_fstream(默认使用std::basic_filebuf)时,单个文件位置将通过seekp()seekg()同时移动;除非您使用单独的变量来存储其中一个位置,以便随后可以跳回该位置,否则您不能独立跟踪放置和读取位置。

第2点的含义是,在fstream上执行读写操作之间,必须刷新缓冲区或者在从输出切换到输入时更改文件位置,并且必须在从输入切换到输出时处于文件末尾或更改文件位置。

有关这些限制的详细信息,请参见C99标准(“fopen函数”)的7.19.5.3/7节,或者C11的7.21.5.3/7节。


谢谢!那么您会建议在什么时候刷新缓冲区呢?如果您正在进行一系列的读取或写入操作,您是否需要在每次读取或写入后都刷新缓冲区,或者将其保持不变是否可以? - raphnguyen
1
@raphnguyen 刷新缓冲区是一项相对昂贵的操作,通常情况下不需要手动刷新,除非你从写入转换到读取并且没有寻找,除非你有充分的理由刷新,否则让实现处理其他刷新。 - user657267

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接