用setw阅读:eof还是不eof?

AnT*_*AnT 7 c++ gcc iostream c++-standard-library visual-c++

请考虑以下简单示例

#include <string>
#include <sstream>
#include <iomanip>

using namespace std;

int main() {
  string str = "string";
  istringstream is(str);
  is >> setw(6) >> str;
  return is.eof();
}
Run Code Online (Sandbox Code Playgroud)

乍一看,由于显式宽度由setw操纵器指定,我希望>>操作符在从输入流成功提取所请求的字符数后完​​成读取字符串.我没有看到它尝试提取第七个字符的任何直接原因,这意味着我不希望流进入eof状态.

当我在MSVC++下运行这个例子时,它按照我的预期工作:读取后流保持良好状态.但是,在GCC中,行为是不同的:流最终处于eof状态.

语言标准,它为此版本的>>运算符提供以下完成条件列表

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

鉴于上述情况,我认为>>运营商没有任何理由将流驱动到eof上述代码中的状态.

然而,这是什么>>运营商在GCC库的实现看起来像

...
__int_type __c = __in.rdbuf()->sgetc();

while (__extracted < __n
       && !_Traits::eq_int_type(__c, __eof)
       && !__ct.is(__ctype_base::space,
                   _Traits::to_char_type(__c)))
{
  if (__len == sizeof(__buf) / sizeof(_CharT))
  {
    __str.append(__buf, sizeof(__buf) / sizeof(_CharT));
    __len = 0;
  }
  __buf[__len++] = _Traits::to_char_type(__c);
  ++__extracted;
  __c = __in.rdbuf()->snextc();
}
__str.append(__buf, __len);

if (_Traits::eq_int_type(__c, __eof))
  __err |= __ios_base::eofbit;
__in.width(0);
...
Run Code Online (Sandbox Code Playgroud)

如您所见,在每次成功迭代结束时,它会尝试__c为下一次迭代准备下一个字符,即使下一次迭代可能永远不会发生.在循环之后,它分析该__c字符的最后一个值并相应地设置eofbit.

所以,我的问题是:eof在上述情况下触发流状态,就像GCC那样 - 从标准的角度来看它是合法的吗?我没有在文档中明确指出它.MSVC和GCC的行为是否合规?或者只是其中一个表现正常?

Sva*_*zen 2

该特定的定义与 的operator>>设置无关eofbit,因为它仅描述操作何时终止,而不描述什么触发特定位。

eofbit标准(草案)中的描述如下:

eofbit - 指示输入操作到达输入序列的末尾;

我想这取决于你想如何解释“达到”。请注意,gcc 实现不会正确设置failbit,其定义为

failbit - 表示输入操作未能读取预期的字符,或者输出操作未能生成所需的字符。

所以我认为eofbit并不一定意味着文件末尾阻碍了任何新字符的提取,只是文件末尾已“到达”。

我似乎找不到“达到”的更准确的描述,所以我想这将是实现定义的。如果这个逻辑是正确的,那么 MSVC 和 gcc 的行为都是正确的。


编辑:特别是,似乎在返回eofbit时设置了。这在参考资料 部分和 参考资料 部分中都有描述。那么现在的问题是:什么时候允许流的当前位置前进?sgetc()eofistreambuf_iteratorbasic_istream::sentry


最终编辑:事实证明 g++ 可能具有正确的行为。

每个字符扫描都会经过<locale>,以便解析不同的字符集、货币格式、时间描述和数字格式。虽然似乎没有关于字符串如何工作的完整描述,但对于数字、时间和金钱的函数如何运作operator>>有非常具体的描述。do_get您可以从远期草案的第 687 页找到它们。

所有这些都是从读取 a ctype(字符的“全局”版本,通过语言环境读取)开始istreambuf_iterator的(对于数字,您可以在草案的第 1018 页找到调用定义)。然后处理ctype,最后迭代器前进。

所以,一般来说,这要求内部迭代器始终指向最后一个读取后的下一个字符;如果情况并非如此,理论上您可以提取比您想要的更多的内容:

string str = "strin1";
istringstream is(str);
is >> setw(6) >> str;
int x;
is >> x;
Run Code Online (Sandbox Code Playgroud)

is如果提取后的当前字符str不在 上eof,则标准将要求x获取值 1,因为对于数字提取,标准明确要求迭代器在第一次读取后前进。

由于这没有多大意义,并且考虑到标准中描述的所有复杂提取都以相同的方式运行,因此对于字符串来说,发生相同的情况是有道理的。这样,is读完6个字符后指针落在 上eofeofbit需要置位。