seekp() 语句似乎是不必要的,但实际上并非如此
The seekp() statement seems to be unnecessary, but actually isn't
以下代码适用于双向流并从文件中查找记录 ID,然后从文件中替换该记录的内容。但是在覆盖内容之前,它会将 put 指针移动到 get 指针的位置。通过tellp()
和tellg()
发现它们在移动前都已经在同一个位置。但是在删除 seekp()
行时,代码不会覆盖数据。
data.txt中的内容:
123 408-555-0394
124 415-555-3422
263 585-555-3490
100 650-555-3434
代码:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
int inID = 263;
const string& inNewNumber = "777-666-3333";
fstream ioData("data.txt");
// Loop until the end of file
while (ioData.good()) {
int id;
string number;
// Read the next ID.
ioData >> id;
// Check to see if the current record is the one being changed.
if (id == inID) {
cout << "get pointer position " << ioData.tellg() << endl; //Displays 39
cout << "put pointer position " << ioData.tellp() << endl; //Displays 39
ioData.seekp(ioData.tellg()); //Commenting this line stops code from working
ioData << " " << inNewNumber;
break;
}
// Read the current number to advance the stream.
ioData >> number;
}
return 0;
}
问题:
当 get 和 put 指针一起移动时,如果 put 指针已经存在,那么使用 seekp()
移动指针的位置有什么必要?
评论中@Revolver_Ocelot链接的问题给出了相关信息。最重要的部分是您必须在读写访问之间刷新或查找。因此,我按以下方式修改了您的代码:
if (id == inID) {
cout << "get pointer position " << ioData.tellg() << endl; //Displays 39
cout << "put pointer position " << ioData.tellp() << endl; //Displays 39
ioData.flush();
cout << "get pointer position " << ioData.tellg() << endl;
cout << "put pointer position " << ioData.tellp() << endl;
ioData.seekp(ioData.tellg()); //Commenting this line stops code from working
ioData << " " << inNewNumber;
break;
}
这给出了以下有趣的输出:
get pointer position 39
put pointer position 39
get pointer position 72
put pointer position 72
(调用 flush()
实际上并没有解决问题。我只是将它添加到您的代码中以向您展示它修改了文件指针。)
我对您的原始代码的假设如下:如果您在先读取文件后写入文件,而没有在两者之间调用 seekp()
,那么文件指针会在数据之前被写入命令修改实际上是写入文件。我假设 write 命令执行某种刷新,并且这会以与我添加到您的代码中的 flush()
命令类似的方式修改文件指针。
当我 运行 在我的 PC 上执行上述代码时,flush()
命令将文件指针移动到位置 72。如果我们从您的原始代码中删除 seekp()
命令,我认为 write 命令还会在实际写入文件之前将文件指针移动到位置 72(或者可能是另一个无效位置)。在这种情况下写入失败,因为位置 72 在文件末尾之后。
因此,需要 ioData.seekp(ioData.tellg());
来确保将文件指针设置到正确的文件位置,因为当您在不调用 seekp()
的情况下切换读取和写入文件时,它可能会发生变化.
this answer的最后一段给出了一些类似的解释。
这是因为如果有人想从输入操作转移到输出操作,这是c++双向流的规则。然后必须使用 seek()
函数来进行这种转换。
此功能是从 c 语言的核心借用的,因为每当有人使用双向流时,程序员可能会使用两个不同的缓冲区,其中一个缓冲区可能用于输入,另一个缓冲区用于输出。现在同步两个缓冲区将是一个性能低效的解决方案。由于大多数时候程序员可能不需要同时使用输入和输出功能,并且程序会无缘无故地为程序员维护两个缓冲区。
因此,作为替代方案,实施了另一种解决方案,让程序员通过调用 seek()
函数显式执行刷新和其他管理。
这意味着我们经常使用的seek()
函数不仅会简单地重新定位文件指针,还会更新缓冲区和流。
另请参阅
why fseek or fflush is always required between reading and writing in the read/write "+" modes
以下代码适用于双向流并从文件中查找记录 ID,然后从文件中替换该记录的内容。但是在覆盖内容之前,它会将 put 指针移动到 get 指针的位置。通过tellp()
和tellg()
发现它们在移动前都已经在同一个位置。但是在删除 seekp()
行时,代码不会覆盖数据。
data.txt中的内容:
123 408-555-0394
124 415-555-3422
263 585-555-3490
100 650-555-3434
代码:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
int inID = 263;
const string& inNewNumber = "777-666-3333";
fstream ioData("data.txt");
// Loop until the end of file
while (ioData.good()) {
int id;
string number;
// Read the next ID.
ioData >> id;
// Check to see if the current record is the one being changed.
if (id == inID) {
cout << "get pointer position " << ioData.tellg() << endl; //Displays 39
cout << "put pointer position " << ioData.tellp() << endl; //Displays 39
ioData.seekp(ioData.tellg()); //Commenting this line stops code from working
ioData << " " << inNewNumber;
break;
}
// Read the current number to advance the stream.
ioData >> number;
}
return 0;
}
问题:
当 get 和 put 指针一起移动时,如果 put 指针已经存在,那么使用 seekp()
移动指针的位置有什么必要?
评论中@Revolver_Ocelot链接的问题给出了相关信息。最重要的部分是您必须在读写访问之间刷新或查找。因此,我按以下方式修改了您的代码:
if (id == inID) {
cout << "get pointer position " << ioData.tellg() << endl; //Displays 39
cout << "put pointer position " << ioData.tellp() << endl; //Displays 39
ioData.flush();
cout << "get pointer position " << ioData.tellg() << endl;
cout << "put pointer position " << ioData.tellp() << endl;
ioData.seekp(ioData.tellg()); //Commenting this line stops code from working
ioData << " " << inNewNumber;
break;
}
这给出了以下有趣的输出:
get pointer position 39
put pointer position 39
get pointer position 72
put pointer position 72
(调用 flush()
实际上并没有解决问题。我只是将它添加到您的代码中以向您展示它修改了文件指针。)
我对您的原始代码的假设如下:如果您在先读取文件后写入文件,而没有在两者之间调用 seekp()
,那么文件指针会在数据之前被写入命令修改实际上是写入文件。我假设 write 命令执行某种刷新,并且这会以与我添加到您的代码中的 flush()
命令类似的方式修改文件指针。
当我 运行 在我的 PC 上执行上述代码时,flush()
命令将文件指针移动到位置 72。如果我们从您的原始代码中删除 seekp()
命令,我认为 write 命令还会在实际写入文件之前将文件指针移动到位置 72(或者可能是另一个无效位置)。在这种情况下写入失败,因为位置 72 在文件末尾之后。
因此,需要 ioData.seekp(ioData.tellg());
来确保将文件指针设置到正确的文件位置,因为当您在不调用 seekp()
的情况下切换读取和写入文件时,它可能会发生变化.
this answer的最后一段给出了一些类似的解释。
这是因为如果有人想从输入操作转移到输出操作,这是c++双向流的规则。然后必须使用 seek()
函数来进行这种转换。
此功能是从 c 语言的核心借用的,因为每当有人使用双向流时,程序员可能会使用两个不同的缓冲区,其中一个缓冲区可能用于输入,另一个缓冲区用于输出。现在同步两个缓冲区将是一个性能低效的解决方案。由于大多数时候程序员可能不需要同时使用输入和输出功能,并且程序会无缘无故地为程序员维护两个缓冲区。
因此,作为替代方案,实施了另一种解决方案,让程序员通过调用 seek()
函数显式执行刷新和其他管理。
这意味着我们经常使用的seek()
函数不仅会简单地重新定位文件指针,还会更新缓冲区和流。
另请参阅 why fseek or fflush is always required between reading and writing in the read/write "+" modes