#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 characters are stored;
- end-of-file occurs on the input sequence;
- isspace(c,is.getloc()) is true for the next available input character 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的行为是否合规?或者只是其中一个表现正常?
解决方法
标准(草案)中eofbit的描述说:
eofbit – indicates that an input operation reached the end of an input sequence;
我想在这里取决于你想要解释“达到”的方式.请注意,gcc实现正确无法设置failbit,其定义为
failbit – indicates that an input operation Failed to read the expected characters,or
that an output operation Failed to generate the desired characters.
所以我认为eofbit并不一定意味着文件的结尾阻碍了任何新字符的提取,只是文件的结尾已经“到达”了.
我似乎无法找到更准确的“达到”描述,所以我想这将是实现定义.如果此逻辑正确,则MSVC和gcc行为都是正确的.
编辑:特别是,当sgetc()返回eof时,似乎eofbit被设置.这在istreambuf_iterator部分和basic_istream :: sentry部分中都有描述.所以现在问题是:什么时候流的当前位置允许前进?
最终编辑:事实证明,g可能具有正确的行为.
每个字符扫描都通过< locale>,以便允许解析不同的字符集,货币格式,时间描述和数字格式.虽然似乎没有关于如何运算符>>的描述.适用于字符串,有关于数字,时间和金钱的do_get函数应如何运作的非常具体的描述.您可以从草案的第687页找到它们.
所有这些都是从istreambuf_iterator读取ctype(字符的“全局”版本,通过locales读取)开始的(对于数字,您可以在草稿的第1018页找到调用定义).然后处理ctype,最后迭代器被提前.
所以,一般来说,这需要内部迭代器始终指向最后一个读取后的下一个字符;如果不是这样的话你理论上可以提取超出你想要的东西:
string str = "strin1"; istringstream is(str); is >> setw(6) >> str; int x; is >> x;
如果当前字符for在str的提取之后不在eof上,那么标准将要求x获得值1,因为对于数字提取,标准明确要求迭代器在第一次读取之后前进.
由于这没有多大意义,并且鉴于标准中描述的所有复杂提取都以相同的方式运行,因此对于字符串来说同样会发生这种情况是有意义的.因此,当读取6个字符后的指针落在eof上时,需要设置eofbit.