假设我们有一个简单的流:
hello
请注意,最后没有额外的\n
,就像文本文件中经常出现的那样。现在,以下简单代码显示在提取单个eof
后,在流上设置了std::string
位。
int main(int argc, const char* argv[])
{
std::stringstream ss("hello");
std::string result;
ss >> result;
std::cout << ss.eof() << std::endl; // Outputs 1
return 0;
}
但是,我不明白为什么会根据标准发生这种情况(我正在阅读C ++ 11 - ISO / IEC 14882:2011(E))。 operator>>(basic_stream<...>&, basic_string<...>&)
被定义为表现得像格式化的输入函数。这意味着它构造了一个sentry
对象,它继续吃掉空白字符。在这个例子中,没有,所以sentry
构造完成没有问题。当转换为bool
时,sentry
对象会给出true
,因此提取器会继续实际提取字符串。
然后将提取定义为:
提取并附加字符,直到出现以下任何一种情况:
存储
n
个字符;- 文件结尾出现在输入序列上;
isspace(c,is.getloc())
适用于下一个可用输入字符 c 。在提取最后一个字符(如果有)之后,调用is.width(0)并销毁sentry对象k。 如果函数没有提取任何字符,则会调用
is.setstate(ios::failbit)
,这可能会抛出ios_base::failure
(27.5.5.4)。
这里没有任何东西实际上导致eof
位被设置。是的,如果提取到达文件结尾,则提取停止,但它不会设置该位。事实上,eof
位只应在我们执行另一个ss >> result;
时设置,因为当sentry
尝试吞噬空格时,会出现以下情况:
如果
is.rdbuf()->sbumpc()
或is.rdbuf()->sgetc()
返回traits::eof()
,则该函数会调用setstate(failbit | eofbit)
然而,这肯定没有发生,因为failbit
没有被设置。
设置eof
位的结果是,邪恶习语while (!stream.eof())
在读取文件时不起作用的唯一原因是因为最后有额外的\n
而且 not 因为eof
位尚未设置。当提取在文件末尾停止时,我的编译器很乐意设置eof
位。
这应该发生吗?或者标准是否意味着应该发生setstate(eofbit)
?
为了更容易,标准的相关部分是:
basic_istream::sentry
[istream :: sentry] 答案 0 :(得分:9)
std::stringstream
是basic_istream
而operator>>
std::string
从中提取“字符”(正如您所发现的那样)。
27.7.2.1班级模板basic_istream
2如果rdbuf() - &gt; sbumpc()或rdbuf() - &gt; sgetc()返回traits :: eof(),那么输入函数除外 明确地另外说明,完成它的行动并且做setstate(eofbit),可能抛出ios_- base :: failure(27.5.5.4),返回之前。
另外,“提取”意味着调用这两个函数。
3两组成员函数签名共享公共属性:格式化输入函数(或 提取器)和未格式化的输入函数。两组输入函数都被描述为它们 通过调用rdbuf() - &gt; sbumpc()或rdbuf() - &gt; sgetc()来获取(或提取)输入字符。他们可能会用 istream的其他公共成员。
因此必须设置eof。
答案 1 :(得分:3)
直观地说,设置EOF位是因为在读取操作期间提取字符串时,流确实命中了文件的末尾。具体来说,它连续从输入流中读取字符,因为它在遇到空白字符之前到达流的末尾而停止。因此,流设置EOF位以标记到达流的末尾。请注意, 与报告失败相同 - 操作已成功完成 - 但EOF位的点不是报告失败。这是为了表明遇到了流的末尾。
我没有特定的规范来支持这一点,但是当我有机会的时候我会尝试寻找一个。