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

来自cppreference.com
< cpp‎ | locale‎ | num 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() 的格式化标志,来自 std::use_facet<std::ctype<CharT>>(str.getloc()) 的字符分类规则,和来自 std::use_facet<std::numpunct<CharT>>(str.getloc()) 的数值标点字符。此函数为所有有格式输入流运算符,如 std::cin >> n; 所调用。

转换作为三个阶段出现:

阶段 1:转换说明符选择

  • 获得输入/输出格式化标志,如同以
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
  • 对于整数类型,在需要时添加长度说明符到转换说明:对于 shortunsigned shorth,对于 longunsigned longl,对于 long longunsigned long longll (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 并不再提取更多字符。
  • 如同以 char_type ct = *in;in 提取下个字符:
    • 如果字符匹配如同用 std::use_facet<std::ctype<CharT>>(str.getloc()).widen() 加宽到本地环境的 char_type"0123456789abcdefxABCDEFX+-" (C++11 前)"0123456789abcdepfxABCDEFPX+-" (C++11 起) 字符之一,那么将它转换到对应的 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。
    • 在任何情况下,检查从前一步骤获得的 char 是否可以用在会为 std::scanf 给定阶段 1 中选择的转换指定符的输入域中。如果可以,那么将它积累到临时缓冲区并重复阶段 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
  • 如果因为测试 in == end 而终止阶段 2,那么执行 err |= std::ios_base::eofbit 设置 eof 位。

返回值

in

注解

在修复 LWG 问题 23LWG 问题 696 前,出现错误时保留 v 不会更改。

在修复 LWG 问题 221 前,因为阶段 2 会过滤掉 'X''x',所以 do_get(int) 会拒绝表示十六进制整数的字符串(例如 "0xA0"),即使它们是对 strtol 的合法输入。

在修复 LWG 问题 1169 前,转换负整数字符串到无符号整数时,某些实现会产生零(因为字符串所表示的值小于目标类型所能表示的值)。

在修复 LWG 问题 2381 前,因为阶段 2 会过滤掉 'P''p',所以 do_get(double) 会拒绝表示带指数部分的十六进制浮点数的字符串(例如 "0x1.23p-10"),即使它们是对 strtod 的合法输入。

因为阶段 2 会过滤掉 'N''i' 这类字符,所以 do_get(double) 会拒绝表示无限或非数的字符串(例如 "NaN""inf"),即使它们是对 strtod 的合法输入。

(C++11 起)

示例

用户定义类型的 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 // 设置 err 可能会抛出异常
    {
        typename std::basic_istream<CharT, Traits>::sentry s(is);
 
        if (s) // 如果流已经输入就绪
        {
            std::use_facet<std::num_get<CharT>>(is.getloc()).get(is, {}, is, err, b.x);
        }
    }
    catch(std::ios_base::failure& error)
    {
        // 处理异常
    }
 
    return is;
}
 
int main()
{
    base b;
    std::cin >> b;
}

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
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'

参阅

提取带格式数据
(std::basic_istream<CharT,Traits> 的公开成员函数)