【问题标题】:tellg() returns -1 only for small filestellg() 仅对小文件返回 -1
【发布时间】:2018-01-05 01:13:42
【问题描述】:

我遇到了一个奇怪的问题。假设我正在读取这样的文件:

std::ifstream in("file.txt", std::ios::binary);
std::string text;
in.seekg(0, std::ios::end);
text.resize(in.tellg());
in.seekg(0, std::ios::beg);
in.read(&text[0], text.size());

当文件包含少于 4 个字符(即 "ab""abc")时会出现问题,但在其他情况下可以正常工作,即 "abcd" 或更大。

为什么tellg 在这种情况下返回-1(最终导致我的字符串抛出std::length_error)?

其他信息:

我正在使用 MSVC 15.5.3(如果不是最新版本,也是更现代的版本之一)。也使用 GCC 5.1 进行了复制。

等效的 C 风格不会出现此错误:

FILE* f = fopen("text.txt", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);

编辑:

failbit 是在第一次调用seekg 之前设置的,这意味着打开文件失败?为什么小于 3 字节的文件会出现这种情况...

【问题讨论】:

  • failbitbadbit-1 返回后的值是多少?
  • @paxdiablo 我用更多信息编辑了这个问题。只设置了failbit。我想在那之后的任何操作都是无效的。
  • 如果无法打开该文件,则该文件不存在或不在当前目录中。文件的大小可能只是巧合。
  • DeiDei,是的,这是正确的,我们现在已经解决了为什么 tellg 返回 -1 的问题。不幸的是,为什么failbit 被设置为开放的根本原因仍然是个谜。我对此的最佳答案是“如果我知道的话” :-) 也许其他人可以帮忙。要快速检查路径,请尝试在代码中使用文件的全名,例如 /path/to/file.txt
  • 在对其进行任何操作之前设置故障位的一种方法是重用流对象。如果之前使用流设置了故障位,并且您在关闭它并重新打开流之前没有清除它(即使打开不同的文件),故障位仍然设置。

标签: c++ c++11 fstream


【解决方案1】:

在几个 cmets 之后,很明显 ifstream 构造函数本身在某种程度上失败了,因为 failbit 甚至在seekg 调用之前设置了。

由于几乎所有 I/O 操作在继续之前首先构造一个哨兵对象,这就是您的操作失败的原因。

所以我有一些建议。

首先,使用文件的完整路径名,以确保您不可能在输入文件所在的目录其他中运行它。

其次,尝试以下在 g++ 5.4(a) 下运行的 complete 程序,看看它是否表现出相同的问题(您的代码虽然是指示性的,但实际上并不是完成)。

#include <iostream>
#include <fstream>

int main() {
    std::ifstream in("/full/path/to/file.txt", std::ios::binary);
    std::cout << "after open, good=" << in.good() << ", bad=" << in.bad()
        << ", fail=" << in.fail() << ", eof=" << in.eof() << std::endl;

    std::cout << "seekg returns " << in.seekg(0, std::ios::end) << std::endl;
    std::cout << "after seek, good=" << in.good() << ", bad=" << in.bad()
        << ", fail=" << in.fail() << ", eof=" << in.eof() << std::endl;

    std::cout << "tellg returns " << in.tellg() << std::endl;
    std::cout << "after tell, good=" << in.good() << ", bad=" << in.bad()
        << ", fail=" << in.fail() << ", eof=" << in.eof() << std::endl;
}

对 2 字节和 10 字节文件都试试这个。

如果这些都没有让您感到高兴,那么应该让 Microsoft 和/或 GNU 意识到这个问题。前者可以here,后者here


为了完整起见,我最初想到的唯一可能性是该文件虽然长三个字节,但在某些方面是无效的。这取决于实际内容,所以如果只是abc,你可以放心地忽略它。

我在想的是一个 Unicode 文件,它有两个字节的 BOM 和一个多字节 Unicode 代码点的第一个字节(例如,UTF-16),或者 UTF-8 的前三个字节四字节码位。

但是,如果您以二进制模式打开它,这似乎难以置信不太可能,因此您可以放心地忽略它。


(a) 对于它的价值,唯一我可以在打开后设置failbit 的方法是删除文件。即使使用空文件也没有出现您描述的问题。

【讨论】:

  • 在对其进行任何操作之前设置故障位的一种方法是重用流对象。如果之前使用流设置了故障位,并且您在关闭它并重新打开流之前没有清除它(即使打开不同的文件),故障位仍然设置。
猜你喜欢
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 2013-12-28
  • 1970-01-01
  • 2011-10-09
相关资源
最近更新 更多