[英]C++ istream tellg()/fail() on eof: behavior change; work-around?
I upgraded my compiler from gcc-4.4 to gcc-4.8 and one project fails miserably stemming from the following (false) assumptions: 我将编译器从gcc-4.4升级到gcc-4.8,由于以下(假)假设,一个项目失败了,但结果非常糟糕:
#include <sstream>
#include <assert.h>
int main()
{
using namespace std;
istringstream iScan;
int num;
//iScan.unsetf(std::ios::skipws);
iScan.str("5678");
iScan >> num;
assert(iScan.tellg() == istringstream::pos_type(4));
assert(!iScan.fail());
assert(!iScan.good());
assert(iScan.eof());
assert(num == 5678);
assert(false && "We passed the above assertions.");
return 0;
}
On gcc-4.4, relevant assertions pass. 在gcc-4.4上,相关断言通过。 On gcc-4.8, tellg() returns -1 and and fail() returns !false, apparently since it hit eof.
在gcc-4.8上,tellg()返回-1,而fail()返回!false,这显然是因为它命中了eof。
My target is MinGW 32-bit as shipped with Qt 5.1 (gcc-4.8). 我的目标是Qt 5.1(gcc-4.8)附带的MinGW 32位。
Questions: 问题:
Further, various online compilers give different behavior. 此外,各种在线编译器会给出不同的行为。 Is that a function of their lib?
这是他们的lib的功能吗?
Other similar but not duplicate questions: 其他类似但不重复的问题:
The body of code, with these assumptions running through it all, is large. 假设所有这些假设都贯穿其中,代码主体很大。
Update: Here's a key part of the answer, which should work across all versions, all compilers: 更新:这是答案的关键部分,它应适用于所有版本,所有编译器:
// istream::tellg() is unreliable at eof(): works w/gcc-4.4, doesn't w/gcc-4.8.
#include <sstream>
#include <assert.h>
using namespace std;
typedef istream::pos_type pos_type;
pos_type reliable_tellg(istream &iScan)
{
bool wasEOF = iScan.eof();
if (wasEOF)
iScan.clear(iScan.rdstate() & ~ios::eofbit); // so tellg() works.
pos_type r = iScan.tellg();
if (wasEOF)
iScan.clear(iScan.rdstate() | ios::eofbit); // restore it.
return r;
}
int main()
{
istringstream iScan;
int num, n2;
//iScan.unsetf(std::ios::skipws);
iScan.str("5678");
assert(!iScan.eof() && !iScan.fail()); // pre-conditions.
assert(reliable_tellg(iScan) == pos_type(0));
iScan >> num;
assert(!iScan.fail());
assert(reliable_tellg(iScan) == pos_type(4));
assert(iScan.eof());
assert(reliable_tellg(iScan) == pos_type(4)); // previous calls don't bungle it.
assert(num == 5678);
iScan >> n2; // at eof(), so this should fail.
assert(iScan.fail());
assert(reliable_tellg(iScan) == pos_type(-1)); // as expected on fail()
assert(iScan.eof());
assert(false && "We passed the above assertions.");
return 0;
}
The behavior you seem to expect is probably wrong. 您似乎期望的行为可能是错误的。 Both C++11 and C++03 start the description of
tellg
with "Behaves as an unformatted input function[...]". C ++ 11和C ++ 03
tellg
“作为无格式的输入函数[...]表现”开始对tellg
进行描述。 An "unformatted input function" starts by constructing a sentry
object, and will fail, doing nothing and returning a failure status, if the sentry
object converts to false
. “未格式化的输入函数”通过构造
sentry
对象开始,并且如果sentry
对象转换为false
,则将失败,不执行任何操作并返回失败状态。 And the sentry
object will convert to false
if the eofbit
is set. 如果设置了
eofbit
则sentry
对象将转换为false
。
The standard is slightly less clear about whether reading the number sets the eofbit
, but only slightly (with the information spread out over several different sections). 该标准对于读取数字是否设置
eofbit
不太清楚,但只是略微(信息分散在几个不同的部分)。 Basically, when inputting a numeric value, the stream (actually, the num_get
facet) must read one character ahead, in order to know where the number ends. 基本上,输入数字值时,流(实际上是
num_get
构面)必须提前读取一个字符,以便知道数字的结尾。 In your case, it will see the end of file when this occurs, and will thus set eofbit
. 在您的情况下,它会在发生这种情况时看到文件的结尾,从而设置
eofbit
。 So your first assert
will fail with a conforming implementation. 因此,您的第一个
assert
将因符合标准的实现而失败。
One could very easily consider this a defect in the standard, or unintentional. 可以很容易地认为这是标准的缺陷,或者是无意的。 It's very easy to imagine some implementations doing the sensible thing (which is what you seem to expect), perhaps because the original implementors didn't realize the full implications in the standard (or unconsciously read it as they thought it should read).
很难想象某些实现会做出明智的事情(这似乎是您所期望的),这可能是因为原始实现者没有意识到标准的全部含义(或者不自觉地阅读了标准的含义)。 I would guess that this is the case for g++, and when they realized that their behavior was non-conformant, they fixed it.
我想这是g ++的情况,当他们意识到自己的行为不符合要求时,便将其修复。
As for work-arounds... I'm not sure what the real problem is, that you're trying to work around. 至于解决方法...我不确定真正的问题是,您正在尝试解决。 But I think that if you clear the error bits before the
tellg
, it should work. 但是我认为,如果您在
tellg
之前清除错误位,它应该可以工作。 (Of course, then iScan.good()
will be true
, and iScan.eof()
false
. But can this really matter?) Just be sure to check that the extraction actually succeeded before you clear the status. (当然,然后
iScan.good()
将为true
,而iScan.eof()
false
。但这真的很重要吗?)在清除状态之前,请务必确保检查提取是否成功。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.