【问题标题】:Strange offset for seekg() while reading text file读取文本文件时 seekg() 的奇怪偏移
【发布时间】:2017-07-10 14:41:22
【问题描述】:

我正在尝试使用此处描述的逻辑Fastest way to read only last line of text file? 获取文件的最后一行,但我遇到了一些奇怪的异常情况:

score.seekg(-2, ios::cur);

将我的流重置为相同的字符,所以我得到了无限循环。但是,将其设置为 -3 效果很好:

fstream score("high_scores.txt"); //open file

if(score.is_open()) //file exist
{   
    score.seekg(0, ios::end);

    char tmp = '~';
    while(tmp != '\n')
    {
        score.seekg(-3, ios::cur);

        if((int)score.tellg() <= 0) //start of file is start of line
        {
            score.seekg(0);
            break;
        }
        tmp = score.get();
        cout << tmp << "-";
    }
}

同样,问题是 - 此代码仅适用于 seekg() 偏移量 -3,而理论上它应该适用于 -2。这可以以某种方式解释吗?文件内容是这样的(文件末尾换行):

28 Mon Jul 10 16:11:24 2017
69 Mon Jul 10 16:11:47 2017
145 Mon Jul 10 16:53:09 2017

我使用的是 Windows,所以现在我明白为什么我需要从文件末尾偏移 -3(以传递 CRLF 字节)。但是让我们考虑第一个字符(从结尾)。

28 Mon Jul 10 16:11:24 2017

所以,流到达7。它提取它,并移动到 CR 字节。那么,如果在下一次循环迭代中我们将其偏移-3,我们将得到0,但不是1!但实际上,我得到了1!并且在-3 偏移量下一切正常。这对我来说是个谜。我无法摆脱它。

【问题讨论】:

  • 请提供minimal reproducible example。很难看出问题在您的问题中的哪个位置。
  • 这很可能是由行结束样式引起的问题 - DOS vs Unix。
  • 在十六进制编辑器或编辑器中查看您的文件,而不是显示文件中每个字符的十进制或十六进制值。另一种方法是以二进制打开文件并以十六进制或十进制显示每个字符。这将显示行尾是 CR、LF (0x0d, 0x0a) 还是仅 LF (0x0a)。
  • @ThomasMatthews 确实,它有 0d 0a 行结尾。 Windows 特定的东西?好的,但这如何影响非结束字符?就像流过去了 0a 和 0d,得到了最后一个字符,移动了 +1 位置。如果现在,我们对当前位置进行 -3 偏移,理论上,我们得到的不是第 2 个而是第 3 个字符(从末尾开始)!但我们获得了第二名……
  • 每一行结束字符都计入定位。 -2 应该定位到最后一个字母“7”。 A -3 应该定位到“1”。至少,这是我的理解。

标签: c++ windows file fstream seekg


【解决方案1】:

我希望这能说明正在发生的事情:

28 Mon Jul 10 16:11:24 2017CL  <- C = CR, L = LF
                       6543210 <- position relative to ios::end
                        | || |
                        | || * Start after seekg(0, ios::end)
                        | *|   After first seekg(-3, ios::cur)
                        |  *   After first get()
                        *      After second seekg(-3, ios::cur)

当您寻找SEEK_END 时,您将流位置指针移动到文件末尾的右侧字节。如果您寻找 -3,您将跳过 CR、LF,并最终到达 '7'。您读取了这个字节,但这会将指针向前移动一个字节。然后你再往回走三个,你最终在'0'

请注意,文件中的行尾实际上是两个字节(CR 和 LF)。只是当您读取它们时,它们会被转换为单个'\n'。但是,当您 seek 时,它只是在实际文件中使用字节偏移量。这就是为什么人们要么建议你从头到尾阅读文件,要么以二进制模式打开文件以消除这种二分法。

【讨论】:

  • 等等,你犯了一个小错误:它不是 seekg(-3, ios::end) 它是 seekg(-3, ios::cur)!理论上,我应该在第二次 seekg 后得到 0,但异常发生了。实际上,我在 7 之后得到 1,然后是 0、2、空格、4、2,等等。就像一切正常。这不是很奇怪吗?我无法解释这一点...就像 get() 在提取 char 后将指针提前 2,而不是 1。但这不可能吗?对吗?
  • 谢谢,我修正了错字!但是如果你得到的是1而不是0,那确实很奇怪。对于普通的fstreamget() 不应将指针提前两个字节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
相关资源
最近更新 更多