c++ - istream 提取的字符 >> double

标签 c++ c++11 iostream facet

示例代码 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);

As in the case of the inserters, these extractors depend on the locale’s num_get<> (22.4.2.1) object to perform parsing the input stream data. These extractors behave as formatted input functions (as described in 27.7.2.2.1). After a sentry object is constructed, the conversion occurs as if performed by the following code fragment:

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:

The details of this operation occur in three stages
— Stage 1: Determine a conversion specifier
— Stage 2: Extract characters from in and determine a corresponding char value for the format expected by the conversion specification determined in stage 1.
— Stage 3: Store results

在第 2 阶段的描述中,我把整个内容都粘贴在这里太长了。然而,它明确表示在尝试转换之前应提取所有字符;并且应该提取以下字符:

  • 任何 0123456789abcdefxABCDEFX+-
  • 语言环境的 decimal_point()
  • 语言环境的 thousands_sep()

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

— For a floating-point value, the function strtold.

The numeric value to be stored can be one of:

— zero, if the conversion function fails to convert the entire field.

这一切似乎都清楚地指定了我的代码的输出应该是 0, 'ipxngh' 。然而,它实际上输出了其他东西。

这是编译器/库错误吗?对于更改第 2 阶段行为的语言环境,我是否忽略了任何规定? (在 another question 中,有人发布了一个系统示例,该系统确实提取了字符,但也提取了不在 N3337 中指定的列表中的 ipxn)。

更新

正如 perreal 所指出的,第 2 阶段的这段文字是相关的:

If discard is true, then if ’.’ has not yet been accumulated, then the position of the character is remembered, but the character is otherwise ignored. Otherwise, if ’.’ has already been accumulated, the character is discarded and Stage 2 terminates. If it is not discarded, then a check is made to determine if c is allowed as the next character of an input field of the conversion specifier returned by Stage 1. If so, it is accumulated.

If the character is either discarded or accumulated then in is advanced by ++in and processing returns to the beginning of stage 2.

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

  • a nonempty sequence of decimal digits optionally containing a decimal-point character, then an optional exponent part as defined in 6.4.4.2;
  • a 0x or 0X, then a nonempty sequence of hexadecimal digits optionally containing a decimal-point character, then an optional binary exponent part as defined in 6.4.4.2;
  • INF or INFINITY, ignoring case
  • NAN or NAN(n-char-sequence opt ), ignoring case in the NAN part, where:

还有

In other than the "C" locale, additional locale-specific subject sequence forms may be accepted.

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

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

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

最佳答案

这是一团糟,因为 gcc/libstdc++ 和 clang/libc++ 的实现很可能都不符合要求。目前还不清楚“进行检查以确定是否允许 c 作为阶段 1 返回的转换说明符的输入字段的下一个字符”是什么意思,但我认为使用短语“下一个字符”表示检查应该是上下文相关(即依赖于已经累积的字符),因此尝试解析,例如 "21abc",应该在 'a' 时停止遇到了。这与 LWG issue 2041 中的讨论一致。 ,在起草 C++11 期间将这句话删除后又将其重新添加到标准中。 libc++ 未能这样做是 bug 17782 .

另一方面,

libstdc++ 拒绝解析 "0xABp-4" 超过 0,这实际上显然不符合标准(它应该解析 "0xAB" 作为 hexfloat,正如 %g 的 C99 fscanf 规范明确允许的那样。

标准不允许接受ipn。参见 LWG issue 2381 .

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

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

关于c++ - istream 提取的字符 >> double,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24689378/

相关文章:

c++ - 在 while 循环的第二次迭代中跳过 Cin

c++ - 关闭exe并从内存运行

c++ - 类和模板伴随类型

header-files - iostream 和 bits/stdc++.h 有什么区别??我们什么时候需要使用它们?

c++ - 如何从 std::basic_ios 为 OS X 上的 clang 获取文件描述符?

Android与C++套接字通信

c++ - Lambda 返回空字符串

c++ - 为什么转发引用需要 std::forward

c++ - Visual Studio 中的内存泄漏

c++ - 有没有办法在 Windows 的 basic_iostream 上获得非锁定流插入/提取?