由istream提取的字符>>双

时间:2014-07-11 02:53:52

标签: c++ c++11 iostream facets

示例代码at Coliru

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    double d; std::string s;

    std::istringstream iss("234cdefipxngh");
    iss >> d;
    iss.clear();
    iss >> s;
    std::cout << d << ", '" << s << "'\n";
}

我在这里阅读N3337(可能与C ++ 11相同)。在[istream.formatted.arithmetic]中我们有(释义):

  

operator>>(double& val);

     

与插入器的情况一样,这些提取器依赖于语言环境的num_get&lt;&gt; (22.4.2.1)对象   执行解析输入流数据。这些提取器表现为格式化的输入函数(如   在27.7.2.2.1中描述。构造sentry对象后,转换就像执行以下代码片段一样:

     

typedef num_get< charT,istreambuf_iterator<charT,traits> > numget;
     iostate err = iostate::goodbit;
     use_facet< numget >(loc).get(*this, 0, *this, err, val);
     setstate(err);

查看22.4.2.1:

  

此操作的详细信息分三个阶段发生    - 阶段1:确定转换说明符
   - 阶段2:从中提取字符并确定格式的相应字符值   由第1阶段确定的转换规范预期。
   - 第3阶段:存储结果

在第2阶段的描述中,对我来说,将整个事情粘贴在这里太长了。但是它清楚地表明在尝试转换之前应该提取所有字符;而且应该提取以下字符:

  • 0123456789abcdefxABCDEFX+-
  • 中的任何一个
  • 区域设置decimal_point()
  • 区域设置thousands_sep()

最后,第3阶段的规则包括:

  

- 对于浮点值,函数strtold

     

要存储的数值可以是以下之一:

     

- 如果转换函数无法转换整个字段,则为零。

这似乎清楚地指明了我的代码的输出应该是0, 'ipxngh'。但是,它实际上输出了其他东西。

这是编译器/库的错误吗?我是否有任何规定可以忽略某个地方以改变第2阶段的行为? (在another question中有人发布了一个实际提取字符的系统示例,但也提取了不在N3337中指定的列表中的ipxn

更新

正如虚幻所指出的,第2阶段的文本是相关的:

  

如果discard为true,那么如果'。'尚未累积,那么该字符的位置   被记住,但这个角色被忽略了。否则,如果'。'已经存在   累积后,角色被丢弃,第2阶段终止。如果没有丢弃,那么a   检查是否允许c被允许作为阶段1返回的转换说明符的输入字段的下一个字符。如果是,则累计。

     

如果该字符被丢弃或累积,那么在++中进行处理   回到第2阶段的开头。

因此,如果角色位于允许的字符列表中,则第2阶段可以终止,但不是%g的有效字符。它没有确切地说,但可能这是指C99中fscanf的定义,它允许:

  
      
  • 非空的十进制数字序列,可选地包含小数点   字符,然后是6.4.4.2中定义的可选指数部分;
  •   
  • 一个0x或0X,然后是一个非空的十六进制数字序列,可选地包含一个   小数点字符,然后是6.4.4.2;
  • 中定义的可选二进制指数部分   
  • INF或INFINITY,忽略大小写
  •   
  • NAN或NAN(n-char-sequence opt),忽略NAN部分中的情况,其中:
  •   

以及

  

除了&#34; C&#34;语言环境,可以接受其他特定于语言环境的主题序列表单。

所以,实际上Coliru输出是正确的;事实上,处理必须尝试验证提取的字符序列,直到%g的有效输入,同时提取每个字符。

下一个问题:在我之前链接的主题中,是否允许在第2阶段接受inp等?

这些是%g的有效字符,但它们不在允许第2阶段读取的原子列表中(即c == 0表示我的最新引号,因此字符既不丢弃也不累积)。

2 个答案:

答案 0 :(得分:5)

这很乱,因为很可能gcc / libstdc ++和clang / libc ++的实现都不符合要求。目前还不清楚“检查是否允许c作为第1阶段返回的转换说明符的输入字段的下一个字符”是指,但我认为使用短语“next character”表示检查应该是上下文敏感(即,取决于已经累积的字符),因此在遇到"21abc"时应该停止尝试解析,例如'a'。这与LWG issue 2041中的讨论是一致的,它在C ++ 11的起草过程中删除后将该句子添加回标准。 libc ++没有这样做是bug 17782

另一方面,另一方面,libstdc ++拒绝解析"0xABp-4"超过0,这实际上显然是基于标准的不一致(它应该将"0xAB"解析为hexfloat,显然C99 fscanf规范允许%g

标准不允许接受ipn。请参阅LWG issue 2381

标准非常精确地描述了处理 - 它必须由指定的代码片段“完成”完成,它不接受这些字符。比较LWG issue 221的分辨率,其中xX添加了num_get0x,因为{{1}}正如后面描述的那样不会解析{{1}}对于整数输入。

Clang / libc ++接受“inf”和“nan”以及hexfloats但不接受“infinity”作为扩展。请参阅bug 19611

答案 1 :(得分:4)

在第2阶段结束时,它说:

  

如果没有丢弃,则进行检查以确定c是否为   允许作为转换输入字段的下一个字符   第1阶段返回的指定者。如果是,则累积。

     

如果该字符被丢弃或累积,则in进入++ in并且处理返回到第2阶段的开头。

因此,a说明符中不允许%g,并且不会累积或忽略它。