用 setw 阅读:to eof or not to eof?

Reading with setw: to eof or not to eof?

提问人:AnT stands with Russia 提问时间:10/21/2014 最后编辑:AnT stands with Russia 更新时间:10/21/2014 访问量:177

问:

请看以下简单示例

#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,为 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);
...

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

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

C 可视化-C++ GCC IOSTREAM C++-标准库

评论

0赞 T.C. 10/21/2014
我不认为规范要求检查列表短路(甚至按该顺序),如果不需要短路,那么第三项(检查下一个可用的输入字符)将要求它准备下一个字符。isspace
0赞 T.C. 10/21/2014
libc++ 同意 MSVC。
0赞 M.M 10/21/2014
我没有标准文本来支持这一点,但我的理解是,是否读取最后一个字符集是可选的。(在阅读最后一个字符后尝试阅读肯定会设置它)。eofbit

答:

2赞 Svalorzen 10/21/2014 #1

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

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

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

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

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

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

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


编辑:特别是,似乎设置了何时返回.本节和本节中对此进行了描述。所以现在的问题是:什么时候允许流的当前位置前进?eofbitsgetc()eofistreambuf_iteratorbasic_istream::sentry


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

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

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

因此,一般来说,这要求内部迭代器始终指向最后一个字符之后的下一个字符;如果不是这种情况,理论上你可以提取比你想要的更多:

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

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

由于这没有多大意义,并且鉴于标准中描述的所有复杂提取都以相同的方式运行,因此对于字符串,同样的情况也是有道理的。因此,由于读取 6 个字符后的指针落在 上,因此需要设置。iseofeofbit

评论

0赞 Svalorzen 10/22/2014
提取或不提取字符对 没有影响。返回时设置;等价地,当返回时,其中是内部流迭代器。 提取字符后停止。由于现在流迭代器指向提取的字符之后的字符,并且该字符是 ,它设置 。eofeofbitsgetceof*iteofitoperator>>neofeofbit