我碰到了一个奇怪的问题。假设我正在读这样一个文件:
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个字节的文件就是这种情况......
在几条评论之后,显然ifstream
构造函数本身在某种程度上失败了,因为failbit
甚至在seekg
调用之前就已经设置好了。
由于几乎所有的I / O操作都是在继续之前首先构造一个哨兵对象,这就是你的操作失败的原因。
所以我有一些建议。
首先,使用文件的完整路径名,以确保您不可能在输入文件所在的目录中运行它。
其次,尝试以下完整的程序,该程序在g ++ 5.4(a)下工作,看它是否表现出同样的问题(你的代码,虽然是指示性的,但并不是真的完整)。
#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;
}
尝试使用两个字节和十个字节的文件。
如果这些都不能给你带来任何快乐,那么应该让微软和/或GNU意识到这个问题。前者可以完成here,后者here。
只是为了完整性,我最初想到的唯一可能性是文件虽然长三个字节,但在某种程度上是无效的。这取决于实际内容,所以如果它只是abc
,你可以放心地忽略它。
我所想的是具有两个字节BOM的Unicode文件和多字节Unicode代码点的第一个字节(例如,UTF-16),或UTF-8四字节的前三个字节。代码点。
但是,如果你在二进制模式下打开它,这似乎非常不可能,所以你可以安全地忽略它。
(a)对于它的价值,我唯一可以在开放后设置failbit
的方法是删除文件。即使使用空文件也没有表现出您所描述的问题。