由 istream 提取的字符>>双倍

Characters extracted by istream >> double

本文关键字:gt 双倍 字符 istream 提取      更新时间:2023-10-16

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。[算术]我们有(意译):

operator>>(double& val);

与插入器的情况一样,这些提取器依赖于区域设置的num_get<>(22.4.2.1)对象执行输入流数据的解析。这些提取器表现为格式化的输入函数(如见27.7.2.2.1)。在构造哨兵对象之后,转换就像由以下代码片段执行一样:

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:确定转换说明符
-第二阶段:从in中提取字符,并确定相应的字符值阶段1中确定的转换规范所期望的。
-阶段3:存储结果

在第二阶段的描述中,我太长了,不能全部粘贴在这里。然而,它清楚地说,在尝试转换之前应该提取所有字符;此外,应该提取以下字符:

  • 任意0123456789abcdefxABCDEFX+-
  • 区域设置的decimal_point() 区域设置的thousands_sep()
最后,第三阶段的规则包括:

-对于浮点数,函数strtold .

要存储的数值可以是:

- 0,如果转换函数转换整个字段失败。

这些似乎都清楚地指定了我的代码的输出应该是0, 'ipxngh'。但是,它实际上输出了其他内容。

这是编译器/库错误吗?是否有任何规定,我忽略了一个地区,以改变阶段2的行为?(在另一个问题中,有人发布了一个系统的例子,该系统确实提取了字符,但也提取了不在N3337中指定的列表中的ipxn)。

更新

perreal指出,第二阶段的这段文字是相关的:

如果discard为真,则If '。’尚未积累,则地位的性格会被记住,但角色会被忽略。否则,if '。已经是。累积,角色被丢弃,第二阶段结束。如果没有被丢弃,则a检查是否允许c作为阶段1返回的转换说明符的输入字段的下一个字符。如果是,则为累积。

如果字符被丢弃或累积,则在前面加++in和处理返回到第2阶段的开始。

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

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

除了"C"语言环境,还可以接受其他特定于语言环境的主题序列形式。

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

下一个问题:是否允许,就像我之前链接到的线程一样,在第二阶段接受i, n, p等?

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

这是一个混乱,因为很可能gcc/libstdc++和clang/libc++的实现都不符合。尚不清楚"检查以确定c是否被允许作为第1阶段返回的转换说明符的输入字段的下一个字符"的意思,但我认为短语"下一个字符"的使用表明检查应该是上下文敏感的(即,依赖于已经积累的字符),因此尝试解析,例如"21abc",应该在遇到'a'时停止。这与LWG第2041期的讨论是一致的,在c++ 11起草期间删除了这句话之后,LWG又将这句话添加到了标准中。libc++的错误是17782。

另一方面,

libstdc++拒绝解析超过0"0xABp-4",这实际上显然是不符合标准的(它应该将"0xAB"解析为十六进制浮点数,C99 fscanf规范明确允许%g)。

标准不允许接收i, p, n。参见LWG issue 2381。

标准非常精确地描述了处理过程——它必须由指定的代码片段"好像"完成,而代码片段不接受这些字符。比较LWG第221期的分辨率,其中他们将xX添加到字符列表中,因为当时描述的num_get不会解析0x的整数输入。

Clang/libc++接受"inf"answers"nan"以及十六进制数,但不接受"infinity"作为扩展名。参见bug 19611。

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

如果没有被丢弃,则检查c是否被丢弃允许作为转换输入字段的下一个字符阶段1返回的说明符。如果是,则为累积。

如果该字符被丢弃或累积,则in被++in推进,处理返回到阶段2的开始。

所以也许a%g说明符中是不允许的,它不会被累积或忽略。