10

假设我们有一个流,其中只包含:

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构造完成没有问题。当转换为 abool时,sentry对象给出true,因此提取器继续进行字符串的实际提取。

然后将提取定义为:

提取并附加字符,直到发生以下任何情况:

  • n存储字符;
  • 文件结束出现在输入序列上;
  • isspace(c,is.getloc())对于下一个可用的输入字符c为真。

提取最后一个字符(如果有)后,调用 is.width(0) 并销毁哨兵对象 k。如果函数没有提取字符,它会调用is.setstate(ios::failbit),这可能会抛出ios_base::failure(27.5.5.4)。

这里实际上没有任何东西会导致该eof位被设置。是的,如果到达文件末尾,提取将停止,但它不会设置该位。实际上,eof只有在执行 another 时才应设置该位ss >> result;,因为当sentry尝试吞噬空白时,会发生以下情况:

如果is.rdbuf()->sbumpc()is.rdbuf()->sgetc()返回traits::eof(),函数调用setstate(failbit | eofbit)

但是,这肯定还没有发生,因为尚未failbit设置。

设置该位的结果是,在读取文件时邪恶习语不起作用eof的唯一原因是因为最后的额外内容,而不是因为该位尚未设置。当提取在文件末尾停止时,我的编译器很乐意设置该位。while (!stream.eof())\neofeof

那么这应该发生吗?还是标准的意思是说setstate(eofbit)应该发生?


为了使它更容易,标准的相关部分是:

  • 21.4.8.9 插入器和提取器 [string.io]
  • 27.7.2.2 格式化输入函数 [istream.formatted]
  • 27.7.2.1.3 类basic_istream::sentry[istream::sentry]
4

2 回答 2

9

std::stringstream是 abasic_istreamoperator>>ofstd::string从中“提取”字符(如您所见)。

27.7.2.1 类模板basic_istream

2 如果 rdbuf()->sbumpc() 或 rdbuf()->sgetc() 返回 traits::eof(),则输入函数,除非另有明确说明,否则将完成其操作并执行 setstate(eofbit),这可能在返回之前抛出 ios_- base::failure (27.5.5.4)。

此外,“提取”意味着调用这两个函数。

3 两组成员函数签名具有共同的属性:格式化输入函数(或提取器)和未格式化输入函数。两组输入函数都被描述为通过调用 rdbuf()->sbumpc() 或 rdbuf()->sgetc() 获取(或提取)输入字符。他们可能会使用 istream 的其他公共成员。

所以必须设置eof。

于 2013-01-29T20:11:45.637 回答
3

直观地说,设置 EOF 位是因为在提取字符串的读取操作期间,流确实到达了文件末尾。具体来说,它不断地从输入流中读取字符,因为它在遇到空白字符之前到达流的末尾而停止。因此,流设置 EOF 位以标记已到达流的末尾。请注意,这与报告失败不同——操作已成功完成——但EOF位的重点是不报告失败这是为了标记遇到流的结尾。

我没有规范的特定部分来支持这一点,尽管当我有机会时我会尝试寻找一个。

于 2013-01-29T20:08:51.090 回答