【问题标题】:std::istream ends unexpectedlystd::istream 意外结束
【发布时间】:2019-07-12 22:53:22
【问题描述】:

我正在尝试分块读取二进制文件。该文件大约为 11 kB。以下代码仅从文件中读取一次,gcount() 函数表示仅读取了 1015 个字符(字节)并且流“已耗尽”并且 while 循环不会继续(尽管缓冲区已正确填充到 @987654323 @):

std::array<int64_t, 256>  HuffmanTree::get_frequencies(std::istream & stream) const
{
    const size_t buffer_size = 4096;
    unsigned char buffer[buffer_size];
    std::array<int64_t, 256> frequencies;
    frequencies.fill(0);

    while (stream)
    {
        stream.read((char *)buffer, buffer_size);
        std::streamsize bytes_read = stream.gcount();

        for (std::streamsize i = 0; i < bytes_read; i++)
        {
            frequencies[buffer[i]]++;
        }
    }

    return frequencies;
}

是什么导致了这种行为,我该如何解决?

编辑:stream.read(...) 仅调用一次,gcount() 返回 1015。但缓冲区包含文件的前 4096 个字节(我也在 Windows 上运行 - VS 2017)

【问题讨论】:

  • 我无法重现。我模拟的输入文件大约为 11 kB,并且该例程能够输入文件的每个字节。我不知道你为什么要为最后一个缓冲区块读取 1015 个字节而不是 ~3072 个字节。如果您在 Windows 上运行,那么我可能会冒险猜测(即 istream 不是二进制模式)。或者如果它是一个输入流,如管道或打开的套接字。
  • 那很奇怪。另外问题是 read() 函数只被调用一次,所以我只得到文件的前 4096 个字节,而没有其他任何东西。流结束说它已读取 1015 个字节。但是缓冲区包含文件中的 4096 个字节。文件的其余部分永远不会被读取。还有如何以二进制模式打开 istream?如果输入文件是二进制文件还是文本文件对我无关紧要怎么办?
  • 你的平台是什么?您使用的是什么 C++ 编译器?您使用的是什么编译器版本?你能用minimal reproducible example 复制吗?您是否尝试过使用调试器单步执行代码?
  • 编辑了问题。我试图逐步完成,那是当我发现 read 函数只被调用一次时。我不明白为什么会发生这种情况(以及为什么它会正确填充缓冲区并说它只读取了 1015 个字节)。
  • "如果输入文件是二进制文件还是文本文件对我来说无关紧要怎么办?"在 Windows 平台上,作为文本处理的文件将处理、解释和翻译字符。可能输入文件在位置 1016 处有一个“文件结尾”字符。

标签: c++ stream istream


【解决方案1】:

我相信当您要读取的字节数少于 4096 并且您调用 stream.read() 时,您的问题就会出现

我使用以下函数,尝试读取 4096,但在失败时进行整理 - 也许它有用?

// Tries to read num bytes from inRaw into buffer (space previously allocated).  When num bytes does not take
// us off the end of the file, then this does the simple obvious thing and returns true.  When num bytes takes
// us over the edge, but things are still sane (they wanted 100 bytes, only 60 bytes to go), then this fills
// as much as possible and leaves things in a nice clean state.  When there are zero bytes to go, this
// return false.
bool safeRead(
    std::ifstream& inRaw,
    char* buffer,
    uint16_t num
)
{
    auto before = inRaw.tellg();
    if (inRaw.read(buffer, num) && inRaw.good() && !inRaw.eof())
    {
        return true;
    }
    else
    {
        if (inRaw.gcount())
        {
            inRaw.clear();
            auto pos = before + std::streamoff(inRaw.gcount());
            inRaw.seekg(pos);
            return true;
        }
        else
        {
            return false;
        }
    }
}

【讨论】:

  • 但是我的文件是 11 kB 并且它永远不会达到我可以读取少于 4096 字节的地步。 while 循环在第一次读取后不会重复(虽然已经读取了 4096 个字节,但它停止读取表示只读取了 1015 个字节)。
  • 您不是在阅读 4096 两次后发现要阅读的内容少于 4096?
  • 不,当单步执行函数时,stream.read(...) 只被调用一次。所以我的缓冲区只包含文件的前 4096 个字节,没有别的。我不明白的是为什么流在我没有读取其余部分时结束,为什么 gcount() 在成功读取 4096 个字节时返回 1015。
  • 听起来好像没有成功读取4096;它只读取 1015 并且文件用完了...确定你给了它一个 11kb 的文件吗?
  • 是的,我确定。缓冲区包含文件的前 4096 个字节。那怎么可能呢?
猜你喜欢
  • 2012-06-23
  • 2015-11-17
  • 2013-12-21
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多