3

我试图掌握解析。

我有一些数据的de-de格式在字符串末尾带有附加信息。

我设法使 de-de 部分正确,但我很难正确解析-%解析。我继续阅读,codecvt但我不明白这个话题。

这是我到目前为止所理解的内容的反映,以及我需要做的一个例子。

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

using namespace std;

#define EXPECT_EQ(actual, expected) { \
    if (actual != expected) \
    { \
        cout << "expected " << #actual << " to be " << expected << " but was " << actual << endl; \
    } \
}

double parse(wstring numstr)
{
    double value;
    wstringstream is(numstr);
    is.imbue(locale("de-de"));
    is >> value;
    return value;
}

int main()
{
    EXPECT_EQ(parse(L"123"), 123); //ok
    EXPECT_EQ(parse(L"123,45"), 123.45); //ok
    EXPECT_EQ(parse(L"1.000,45"), 1000.45); //ok
    EXPECT_EQ(parse(L"2,390%"), 0.0239); //% sign at the end
    EXPECT_EQ(parse(L"1.234,56-"), -1234.56); //- sign at the end
}

输出是:

expected parse(L"2,390%") to be 0.0239 but was 2.39
expected parse(L"1.234,56-") to be -1234.56 but was 1234.56

我怎样才能灌输我的流,以便它像我需要的那样读取-和签名?%

4

2 回答 2

2

codecvt刻面是看这里的错误位置。该codecvt构面仅用于处理将字符的外部表示转换为同一字符的内部表示(例如,文件中的 UTF-8,内部的 UTF-32/UCS-4)。

对于像这样解析数字,您正在寻找num_get方面。基本思想是,您将创建一个派生自该类(至少)std::num_get覆盖do_get您关心的数字类型的类。

在典型情况下,您只对少数类型(例如,long long 和 long double)进行“真正的”实现,并将所有较小类型的函数委托给这些类型,然后将结果转换为目标类型。

这是一个相当简单的num_get方面。目前,它只尝试提供对 type 的特殊处理double。为了避免这个例子变得太长,我稍微简化了处理:

  • 它不会尝试解析数字上的指数(例如,1e99 中的“99”)。
  • 它不会尝试处理%-( 但会-%) 的后缀。
  • 硬编码将 ',' 视为小数点和 '.' 作为千位分隔符。
  • 它没有尝试检查数千个分隔符。例如,1,,,3将解析为13.

在这些限制范围内,这里有一些代码:

#include <ios>
#include <string>
#include <locale>
#include <iostream>
#include <sstream>
#include <iterator>
#include <cctype>

using namespace std;

template <class charT, class InputIterator = istreambuf_iterator<charT> >
class read_num : public std::num_get < charT > {
public:
    typedef charT char_type;
    typedef InputIterator iter_type;
protected:
    iter_type do_get(iter_type in, iter_type end, ios_base& str, ios_base::iostate& err, double& val) const {
        double ret = 0.0;

        bool negative = false;
        using uc = std::make_unsigned<charT>::type;

        while (std::isspace((uc)*in))
            ++in;
        if (*in == '-') {
            negative = true;
            ++in;
            while (std::isspace((uc)*in))
                ++in;
        }
        while (std::isdigit((uc)*in)) {
            ret *= 10;
            ret += *in - '0';
            ++in;
            if (*in == '.')
                ++in;
        }
        if (*in == ',') {
            ++in;
            double place = 10.0;
            while (std::isdigit((uc)*in)) {
                ret += (*in - '0') / place;
                place *= 10;
                ++in;
            }
        }
        if (*in == '-') {
            negative = true;
            ++in;
        }
        if (*in == '%') {
            ret /= 100.0;
            ++in;
        }
        if (negative)
            ret = -ret;
        val = ret;
        return in;
    }
};

实际上,在您可能不想以这种方式做事的情况下——您可能希望委派给现有方面以正确读取数字,然后在它解析的最后,查找 a-和/或%并做出反应适当的(并且可能诊断错误,例如,如果您发现前导和尾随'-')。

于 2015-09-29T22:57:50.737 回答
2

我会正面解决这个问题:让我们在这里掌握解析。

无论如何,你最终会在某个地方写下它,所以我会忘记首先创建一个(昂贵的)字符串流的需要。

首选武器:提升精神

笔记,

  • 我直接使用它的迭代器解析字符串。我的代码对于所使用的浮点数类型非常通用。

  • 您几乎可以搜索替换double为例如 boost::multiprecision::cpp_dec_float(或使其成为模板参数)并进行解析。因为我预测您需要解析 十进制浮点数,而不是二进制浮点数。您正在失去转换的准确性。

更新:扩展样本Live On Coliru

简单语法

在它的核心,语法非常简单:

if (parse(numstr.begin(), numstr.end(), mynum >> matches['-'] >> matches['%'],
            value, sign, pct)) 
{
    if (sign) value = -value;
    if (pct)  value /= 100;

    return value;
}

你有它。当然,我们需要定义mynum以便它按预期解析无符号实数:

using namespace qi;
real_parser<double, de_numpolicy<double> > mynum;

魔法:real_policies<>

该文档在很大程度上解释了如何使用real_policies. 这是我提出的政策:

template <typename T>
    struct de_numpolicy : qi::ureal_policies<T>
{
    //  No exponent
    template <typename It>                static bool parse_exp(It&, It const&)          { return false; } 
    template <typename It, typename Attr> static bool parse_exp_n(It&, It const&, Attr&) { return false; } 

    //  Thousands separated numbers
    template <typename It, typename Attr>
    static bool parse_n(It& first, It const& last, Attr& attr)
    {
        qi::uint_parser<unsigned, 10, 1, 3> uint3;
        qi::uint_parser<unsigned, 10, 3, 3> uint3_3;

        if (parse(first, last, uint3, attr)) {
            for (T n; qi::parse(first, last, '.' >> uint3_3, n);)
                attr = attr * 1000 + n;

            return true;
        }

        return false;
    }

    template <typename It>
        static bool parse_dot(It& first, It const& last) {
            if (first == last || *first != ',')
                return false;
            ++first;
            return true;
        }
};

完整演示

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <iostream>


#define EXPECT_EQ(actual, expected) { \
    double v = (actual); \
    if (v != expected) \
    { \
        std::cout << "expected " << #actual << " to be " << expected << " but was " << v << std::endl; \
    } \
}

namespace mylib {
    namespace qi = boost::spirit::qi;

    template <typename T>
        struct de_numpolicy : qi::ureal_policies<T>
    {
        //  No exponent
        template <typename It>                static bool parse_exp(It&, It const&)          { return false; } 
        template <typename It, typename Attr> static bool parse_exp_n(It&, It const&, Attr&) { return false; } 

        //  Thousands separated numbers
        template <typename It, typename Attr>
        static bool parse_n(It& first, It const& last, Attr& attr)
        {
            qi::uint_parser<unsigned, 10, 1, 3> uint3;
            qi::uint_parser<unsigned, 10, 3, 3> uint3_3;

            if (parse(first, last, uint3, attr)) {
                for (T n; qi::parse(first, last, '.' >> uint3_3, n);)
                    attr = attr * 1000 + n;

                return true;
            }

            return false;
        }

        template <typename It>
            static bool parse_dot(It& first, It const& last) {
                if (first == last || *first != ',')
                    return false;
                ++first;
                return true;
            }
    };

    template<typename Char, typename CharT, typename Alloc>
    double parse(std::basic_string<Char, CharT, Alloc> const& numstr)
    {
        using namespace qi;
        real_parser<double, de_numpolicy<double> > mynum;

        double value;
        bool sign, pct;

        if (parse(numstr.begin(), numstr.end(), mynum >> matches['-'] >> matches['%'],
                    value, sign, pct)) 
        {
            // std::cout << "DEBUG: " << std::boolalpha << " '" << numstr << "' -> (" << value << ", " << sign << ", " << pct << ")\n";
            if (sign) value = -value;
            if (pct)  value /= 100;

            return value;
        }

        assert(false); // TODO handle errors
    }

} // namespace mylib

int main()
{
    EXPECT_EQ(mylib::parse(std::string("123")),       123);      // ok
    EXPECT_EQ(mylib::parse(std::string("123,45")),    123.45);   // ok
    EXPECT_EQ(mylib::parse(std::string("1.000,45")),  1000.45);  // ok
    EXPECT_EQ(mylib::parse(std::string("2,390%")),    0.0239);   // %  sign at the end
    EXPECT_EQ(mylib::parse(std::string("1.234,56-")), -1234.56); // -  sign at the end
}

如果您取消注释“DEBUG”行,它会打印:

DEBUG:  '123' -> (123, false, false)
DEBUG:  '123,45' -> (123.45, false, false)
DEBUG:  '1.000,45' -> (1000.45, false, false)
DEBUG:  '2,390%' -> (2.39, false, true)
DEBUG:  '1.234,56-' -> (1234.56, true, false)
于 2015-09-30T01:06:35.310 回答