stringstream无符号输入验证 [英] stringstream unsigned input validation

查看:221
本文介绍了stringstream无符号输入验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写程序的一部分,解析和验证程序控制台参数中的一些用户输入。

I'm writing part of program which parses and validates some user input in program console arguments. I choose to use stringstream for that purpose, but encounter a problem with unsigned types reading.

下一个模板用于从给定的字符串中读取请求的类型:

Next template is intended for reading requested type from given string:

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

using std::string;
using std::stringstream;
using std::cout;
using std::endl;

template<typename ValueType>
ValueType read_value(string s)
{   
    stringstream ss(s);
    ValueType res;
    ss >> res;
    if (ss.fail() or not ss.eof())
        throw string("Bad argument: ") + s;
    return res;
}
// +template specializations for strings, etc. 

int main(void)
{   
    cout << read_value<unsigned int>("-10") << endl;
}   

如果类型是unsigned并且输入字符串包含负数, throw(由 ss.fail()= true 引起)。但是stringstream生成casted到无符号类型值(在写样本中为4294967286)。

In case type is unsigned and input string contains negative number I expect to see exception throw (caused by ss.fail() = true). But stringstream produces casted to unsigned type value (4294967286 in written sample).

如何修复这个示例以实现期望的行为
我明白,它可以用简单的第一个符号检查,但我可以把前导空格为例。我可以写自己的解析器,但不相信问题是如此不可预测和标准库无法解决它。

How can be this sample fixed to achieve desired behavior (preferable without fallback to c functions)? I understand that it can be done with simple first symbol check but I can put leading spaces for example. I can write own parser but don't believe that problem is so unpredictable and standard library unable to solve it.

隐藏在stringstream运算符深入的无符号类型的函数是strtoull和strtoul。它们以描述的方式工作,但是所提及的功能是低级的。为什么stringstream不提供一些验证级别? (我只是希望我错了,它只是一些动作需要启用这个)。

Functions hidden in deep of stringstream operators for unsigned types are strtoull and strtoul. They work in described manner but mentioned functions are low-level. Why stringstream do not provide some validation level? (I just hope I'm wrong and it does but some movements required to enable this).

推荐答案

A num_get facet以支持对signedness的显式检查。拒绝以' - '开头(对于无符号类型的空格后面)的任何非零数,并使用默认C语言环境的 num_get 进行实际转换。

A num_get facet to support the explicit check for signedness. Rejects any non-zero number beginning with a '-' (after white-spaces) for unsigned types and uses the default C locale's num_get to do the actual conversion.

#include <locale>
#include <istream>
#include <ios>
#include <algorithm>

template <class charT, class InputIterator = std::istreambuf_iterator<charT> >
class num_get_strictsignedness : public std::num_get <charT, InputIterator>
{
public:
    typedef charT char_type;
    typedef InputIterator iter_type;

    explicit num_get_strictsignedness(std::size_t refs = 0)
        : std::num_get<charT, InputIterator>(refs)
    {}
    ~num_get_strictsignedness()
    {}

private:
    #define DEFINE_DO_GET(TYPE) \
        virtual iter_type do_get(iter_type in, iter_type end,      \
            std::ios_base& str, std::ios_base::iostate& err,       \
            TYPE& val) const override                              \
        {  return do_get_templ(in, end, str, err, val);  }         // MACRO END

    DEFINE_DO_GET(unsigned short)
    DEFINE_DO_GET(unsigned int)
    DEFINE_DO_GET(unsigned long)
    DEFINE_DO_GET(unsigned long long)

    // not sure if a static locale::id is required..

    template <class T>
    iter_type do_get_templ(iter_type in, iter_type end, std::ios_base& str,
                           std::ios_base::iostate& err, T& val) const
    {
        using namespace std;

        if(in == end)
        {
            err |= ios_base::eofbit;
            return in;
        }

        // leading white spaces have already been discarded by the
        // formatted input function (via sentry's constructor)

        // (assuming that) the sign, if present, has to be the first character
        // for the formatting required by the locale used for conversion

        // use the "C" locale; could use any locale, e.g. as a data member

        // note: the signedness check isn't actually required
        //       (because we only overload the unsigned versions)
        bool do_check = false;
        if(std::is_unsigned<T>{} && *in == '-')
        {
            ++in;  // not required
            do_check = true;
        }

        in = use_facet< num_get<charT, InputIterator> >(locale::classic())
                 .get(in, end, str, err, val);

        if(do_check && 0 != val)
        {
            err |= ios_base::failbit;
            val = 0;
        }

        return in;
    }
};

使用示例:

#include <sstream>
#include <iostream>
int main()
{
    std::locale loc( std::locale::classic(),
                     new num_get_strictsignedness<char>() );
    std::stringstream ss("-10");
    ss.imbue(loc);
    unsigned int ui = 42;
    ss >> ui;
    std::cout << "ui = "<<ui << std::endl;
    if(ss)
    {
        std::cout << "extraction succeeded" << std::endl;
    }else
    {
        std::cout << "extraction failed" << std::endl;
    }
}

注意:


  • 在免费商店上的分配不是必需的,你可以使用eg一个(静态)局部变量,您可以在ctor

  • 中为每个要支持的字符类型使用 1 char wchar_t charXY_t ),添加自己的facet(可以是 num_get_strictsignness 模板的不同实例化)

  • - 0 code>

  • the allocation on the free store is not required, you could use e.g. a (static) local variable where you initialize the ref counter with 1 in the ctor
  • for every character type you want to support (like char, wchar_t, charXY_t), you need to add an own facet (can be different instantiations of the num_get_strictsignedness template)
  • "-0" is accepted

这篇关于stringstream无符号输入验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆