命名空间
变体
操作

std::num_get<CharT,InputIt>::get, std::num_get<CharT,InputIt>::do_get

来自 cppreference.com
< cpp‎ | locale‎ | num get
 
 
 
std::num_get
成员函数
num_get::getnum_get::do_get
 
(1)
public:

iter_type get( iter_type in, iter_type end, std::ios_base& str,

               std::ios_base::iostate& err, bool& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, long& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, long long& v ) const;
(自 C++11 起)
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, unsigned short& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, unsigned int& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, unsigned long& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, unsigned long long& v ) const;
(自 C++11 起)
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, float& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, double& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, long double& v ) const;
iter_type get( iter_type in, iter_type end, std::ios_base& str,
               std::ios_base::iostate& err, void*& v ) const;
(2)
protected:

virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,

                          std::ios_base::iostate& err, bool& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, long& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, long long& v ) const;
(自 C++11 起)
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, unsigned short& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, unsigned int& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, unsigned long& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,

                          std::ios_base::iostate& err,

                          unsigned long long& v ) const;
(自 C++11 起)
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, float& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, double& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, long double& v ) const;
virtual iter_type do_get( iter_type in, iter_type end, std::ios_base& str,
                          std::ios_base::iostate& err, void*& v ) const;
1) 公共成员函数,调用最派生类的成员函数 do_get
2) 从输入迭代器 in 中读取字符,并生成 v 类型的值,同时考虑来自 str.flags() 的 I/O 流格式化标志,来自 std::use_facet<std::ctype<CharT>>(str.getloc()) 的字符分类规则,以及来自 std::use_facet<std::numpunct<CharT>>(str.getloc()) 的数字标点符号字符。此函数由所有格式化输入流运算符调用,例如 std::cin >> n;

转换分三个阶段进行

内容

[编辑] 阶段 1:转换说明符选择

  • 获取 I/O 格式化标志,如同以下代码所示:
fmtflags basefield = (str.flags() & std::ios_base::basefield);
fmtflags boolalpha = (str.flags() & std::ios_base::boolalpha);
  • 如果 v 的类型是整数类型,则选择以下五种选项中的第一个适用选项:
如果 basefield == oct,则使用转换说明符 %o
如果 basefield == hex,则使用转换说明符 %X
如果 basefield == 0,则使用转换说明符 %i
如果 v 的类型是有符号类型,则使用转换说明符 %d
如果 v 的类型是无符号类型,则使用转换说明符 %u
  • 对于整数类型,如果需要,会在转换说明符中添加长度修饰符:h 用于 shortunsigned shortl 用于 longunsigned long, ll 用于 long longunsigned long long(自 C++11 起)
  • 如果 v 的类型为 float,将使用转换说明符 %g
  • 如果 v 的类型为 double,将使用转换说明符 %lg
  • 如果 v 的类型为 long double,将使用转换说明符 %Lg
  • 如果 v 的类型为 void*,将使用转换说明符 %p
  • 如果 v 的类型为 boolboolalpha == 0,则将按照 v 类型为 long 的方式进行处理,步骤 3 中要存储到 v 中的值除外。
  • 如果 v 的类型为 boolboolalpha != 0,则以下内容将替换步骤 2 和 3
    • 从输入迭代器 in 中获取的连续字符将与从 std::use_facet<std::numpunct<CharT>>(str.getloc()).falsename()std::use_facet<std::numpunct<CharT>>(str.getloc()).truename() 中获取的字符序列进行匹配,仅在必要时进行匹配以识别唯一匹配。仅在需要获取字符时,将输入迭代器 inend 进行比较。
    • 如果目标序列是唯一匹配的,则将 v 设置为相应的 bool 值。否则,将 false 存储到 v 中,并将 std::ios_base::failbit 分配给 err。如果在输入结束之前无法找到唯一匹配项(in == end),则执行 err |= std::ios_base::eofbit

[编辑] 步骤 2:字符提取

  • 如果 in == end,则步骤 2 会立即终止,不会提取任何其他字符。
  • in 中提取下一个字符,就好像执行了 char_type ct = *in;
    • 如果字符与以下字符中的一个匹配 "0123456789abcdefxABCDEFX+-"(直到 C++11)"0123456789abcdefpxABCDEFPX+-"(自 C++11 起),并使用 std::use_facet<std::ctype<CharT>>(str.getloc()).widen() 扩展到区域设置的 char_type,则将其转换为相应的 char
    • 如果字符与小数点分隔符匹配(std::use_facet<std::numpunct<CharT>>(str.getloc()).decimal_point())),则将其替换为 '.'
    • 如果字符与千位分隔符匹配(std::use_facet<std::numpunct<CharT>>(str.getloc()).thousands_sep()),且千位分隔符正在使用中(由 std::use_facet<std::numpunct<CharT>>(str.getloc()).grouping().length() != 0 确定),则如果尚未累积小数点 '.',则会记住该字符的位置,否则会忽略该字符。如果已经累积了小数点,则会丢弃该字符,步骤 2 终止。
    • 在任何情况下,都会检查在步骤 1 中选择的转换说明符下,在由 std::scanf 解析的输入字段中是否允许从前面的步骤中获取的 char。如果允许,则将其累积到一个临时缓冲区中,步骤 2 继续执行。如果不允许,则步骤 2 终止。

[编辑] 步骤 3:转换和存储

  • 在步骤 2 中累积的 char 序列将转换为数值
输入将按照 std::scanf 的规则进行解析。
(直到 C++11)
输入将按照以下方式进行解析
(自 C++11 起)
  • 如果转换函数无法转换整个字段,则将 0 存储到 v 中。
  • 如果 v 的类型为带符号的整数类型,并且转换函数的结果是超出其范围的正数或负数,则分别将可表示的最大正数或负数存储到 v 中。
  • 如果 v 的类型为无符号整数类型,并且转换函数的结果是超出其范围的值,则将可表示的最大正数存储到 v 中。
  • 在任何情况下,如果转换函数失败,则将 std::ios_base::failbit 分配给 err
  • 否则,将转换的数值结果存储到 v 中。
    • 如果 v 的类型为 bool 且 boolalpha 未设置,则如果要存储的值为 0,则存储 false,如果要存储的值为 1,则存储 true,对于任何其他值,将 std::ios_base::failbit 分配给 err 并存储 true
  • 在此之后,将检查数字分组。如果在阶段 2 中丢弃的任何千位分隔符的位置与 std::use_facet<std::numpunct<CharT>>(str.getloc()).grouping() 提供的分组不匹配,则将 std::ios_base::failbit 分配给 err
  • 如果阶段 2 由测试 in == end 终止,则执行 err |= std::ios_base::eofbit 以设置 eof 位。

[edit] 返回值

in

[edit] 注释

在解决 LWG issue 23LWG issue 696 之前,如果发生错误,则 v 保持不变。

在解决 LWG issue 221 之前,表示十六进制整数的字符串(例如 "0xA0")会被 do_get(int) 拒绝,即使它们是 strtol 的有效输入,因为阶段 2 会过滤掉字符 'X''x'

在解决 LWG issue 1169 之前,将负数字符串转换为无符号整数可能会产生零(因为字符串表示的值小于目标类型可以表示的值)。

在解决 LWG issue 2381 之前,表示带指数的十六进制浮点数的字符串(例如 "0x1.23p-10")会被 do_get(double) 拒绝,即使它们是 strtod 的有效输入,因为阶段 2 会过滤掉字符 'P''p'

表示无穷大或非数字的字符串(例如 "NaN""inf")会被 do_get(double) 拒绝,即使它们是 strtod 的有效输入,因为阶段 2 会过滤掉诸如 'N''i' 之类的字符。

(自 C++11 起)

[edit] 示例

为用户定义类型实现 operator>>

#include <iostream>
#include <iterator>
#include <locale>
 
struct base { long x; };
 
template<class CharT, class Traits>
std::basic_istream<CharT, Traits>&
    operator >>(std::basic_istream<CharT, Traits>& is, base& b)
{
    std::ios_base::iostate err = std::ios_base::goodbit;
 
    try // setting err could throw
    {
        typename std::basic_istream<CharT, Traits>::sentry s(is);
 
        if (s) // if stream is ready for input
            std::use_facet<std::num_get<CharT>>(is.getloc()).get(is, {}, is, err, b.x);
    }
    catch (std::ios_base::failure& error)
    {
        // handle the exception
    }
 
    return is;
}
 
int main()
{
    base b;
    std::cin >> b;
}

[edit] 缺陷报告

以下行为更改缺陷报告已追溯应用于以前发布的 C++ 标准。

DR 应用于 已发布的行为 正确行为
LWG 17 C++98 解析文本布尔值的过程有误 已修正
LWG 18 C++98 缺少采用 bool& 值的 get 的重载 已添加
LWG 23 C++98 溢出输入导致未定义行为 已处理溢出
LWG 154 C++98 用于 double 的转换说明符为 %g(与 float 相同) 已更改为 %lg
LWG 221 C++98 do_get 未解析 'x''X',而 strtol 解析了它们 已解析 'x''X'
LWG 275 C++98 get 有一个采用 short& 值的重载,而不是 float& 已修正
LWG 358 C++98 小数点后的千位分隔符被忽略 如果遇到小数点后的千位分隔符,则阶段 2 终止
LWG 696 C++98 转换失败时结果保持不变 已设置为零
LWG 1169 C++98 浮点类型之间的溢出处理不一致 已变得一致
strtof/strtod 一致
LWG 2381 C++11 do_get 未解析 'p''P',而 strtod 解析了它们 已解析 'p''P'

[edit] 另请参阅

提取格式化的数据
(std::basic_istream<CharT,Traits> 的公有成员函数) [edit]