用setw阅读:eof还是不eof?

时间:2014-10-20 18:14:54

标签: c++ visual-c++ gcc iostream c++-standard-library

考虑以下简单示例

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

using namespace std;

int main() {
  string str = "string";
  istringstream is(str);
  is >> setw(6) >> str;
  return is.eof();
}

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

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

语言标准,它为此版本的>>运算符

提供以下完成条件列表
  
      
  • 存储n个字符;
  •   
  • 文件结尾出现在输入序列上;
  •   
  • isspace(c,is.getloc())对于下一个可用输入字符c为真。
  •   

鉴于上述情况,我没有看到>>运算符在上述代码中将流驱动为eof状态的任何原因。

但是,这就是>>运算符implementation in GCC library的样子

...
__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);
...

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

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

1 个答案:

答案 0 :(得分:2)

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

标准(草稿)中eofbit的说明如下:

  

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

我想这里取决于你想要解释的方式&#34;达到&#34;。请注意,gcc实现正确无法设置failbit,其定义为

  

failbit - 表示输入操作无法读取预期的字符,或   输出操作无​​法生成所需的字符。

所以我认为eofbit并不一定意味着文件的结尾阻碍了任何新字符的提取,只是文件的结尾已经达到&#34;。

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


编辑:特别是,当eofbit返回sgetc()时,eof似乎已设置。这在istreambuf_iterator部分和basic_istream::sentry部分中都有描述。所以现在问题是:什么时候流的当前位置允许前进?


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

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

所有这些都是从ctype读取istreambuf_iterator(&#34;全局&#34;字符版本,通过区域设置读取)开始的(对于数字,您可以找到草案第1018页的电话定义)。然后处理ctype,最后迭代器被提前。

因此,通常,这需要内部迭代器始终指向最后一个读取后的下一个字符;如果情况并非如此,理论上你可以提取超出你想要的东西:

string str = "strin1";
istringstream is(str);
is >> setw(6) >> str;
int x;
is >> x;

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

由于这没有多大意义,并且鉴于标准中描述的所有复杂提取都以相同的方式运行,因此对于字符串来说同样会发生这种情况是有意义的。因此,当读取6个字符后is的指针落在eof上时,需要设置eofbit