【问题标题】:The seekp() statement seems to be unnecessary, but actually isn'tseekp() 语句似乎是不必要的,但实际上并非如此
【发布时间】:2023-03-14 17:35:01
【问题描述】:

以下代码适用于双向流,并从文件中查找记录 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() 来移动它的位置吗?

【问题讨论】:

标签: c++ file-access


【解决方案1】:

cmets 中@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;
}

这给出了以下有趣的输出:

获取指针位置 39
将指针位置 39
获取指针位置 72
把指针位置72

(调用flush()实际上并不能解决问题。我只是将它添加到您的代码中,以便向您展示它修改了文件指针。)

我对您的原始代码的假设如下:如果您先读取文件后写入文件,而不在两者之间调用seekp(),则文件指针会在数据实际写入之前被写入命令修改文件。我假设 write 命令执行某种刷新,并且这会以与我添加到您的代码中的 flush() 命令类似的方式修改文件指针。

当我在我的 PC 上运行上面的代码时,flush() 命令将文件指针移动到位置 72。如果我们从原始代码中删除 seekp() 命令,我认为写入命令也会移动文件在实际写入文件之前指向位置 72(或者可能是另一个无效位置)的指针。在这种情况下写入失败,因为位置 72 在文件末尾之后。

因此,需要ioData.seekp(ioData.tellg()); 来确保将文件指针设置到正确的文件位置,因为当您在读取和写入文件之间切换而不调用seekp() 时,它可能会发生变化。

this answer的最后一段给出了类似的解释。

【讨论】:

  • 我没有得到你的 eof 点,我的编译器在所有文件位置显示 39,写入后移动到 52(位于下一条记录之前)
  • ioData.flush();好像没用
  • 但是flush后文件指针还在39位,写入文件后改变了位置
  • 感谢您的反馈。我觉得我的回答现在更有意义了。非常感谢您接受它!
  • 非常感谢您在我遇到麻烦时帮助我:)
【解决方案2】:

这是因为如果有人想从输入操作转移到输出操作,这是 c++ 双向流的规则。然后必须使用seek() 函数来进行这种转变。

此功能是从 c 语言的核心借用的,因为每当有人使用双向流时,程序员可能会使用两个不同的缓冲区,其中一个缓冲区可能用于输入,另一个缓冲区用于输出。现在同步两个缓冲区将是一种性能低效的解决方案。由于大多数时候程序员可能不需要同时使用输入和输出功能,并且程序会无缘无故地为程序员维护两个缓冲区。

因此,作为替代方案,实现了另一种解决方案,让程序员通过调用seek() 函数显式执行刷新和其他管理。

这意味着我们经常使用的seek()函数不仅会重新定位文件指针,还会更新缓冲区和流。

另请参阅 why fseek or fflush is always required between reading and writing in the read/write "+" modes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-02
    • 2016-10-18
    • 2020-06-26
    • 2015-12-18
    • 2016-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多