boost :: spirit和strict_real_policies无法解析太长的整数

时间:2018-11-20 19:20:04

标签: c++ boost-spirit

我必须处理文本格式的很长的整数-直到它们不适合32位int为止。 我需要将此类文本解析为

boost::variant<int, double>.

因此,如果一个整数有一个长整数到大整数,则需要加倍。请参见下面的示例。它不会解析名称值对

MUESR1 = 411100000000000. 

如何解决?

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iterator>


namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;

typedef boost::variant<int, double> VALUE;
typedef std::pair<std::string, VALUE> PAIR;
typedef std::map<std::string, VALUE> PAIRS;
template<typename Iterator>
struct parameters:qi::grammar<Iterator, PAIRS(), ascii::space_type>
{
    qi::rule<Iterator, std::string(), ascii::space_type> m_sName;
    qi::rule<Iterator, VALUE(), ascii::space_type> m_sValue;
    qi::rule<Iterator, PAIR(), ascii::space_type> m_sNameValue;
    qi::rule<Iterator, PAIRS(), ascii::space_type> m_sRoot;
    qi::real_parser<double, qi::strict_real_policies<double> > m_sReal;

    parameters(void)
        :parameters::base_type(m_sRoot)
    {   m_sName %= qi::lexeme[qi::char_("a-zA-Z_") >> *qi::char_("a-zA-z_0-9")];
        m_sValue %= m_sReal | spirit::int_;
        m_sNameValue %= m_sName >> qi::lit('=') >> m_sValue >> -qi::lit('\n');
        m_sRoot %= m_sNameValue >> *m_sNameValue;
    }
};


int main(int, char**)
{
static const char s_ap[] = "\
MUEPH1 = 7.014158 MUEPHW=  -0.3 MUEPWP = 0.23 MUEPHL=  -0.72 MUEPLP = 3.4 MUEPHS = 2.976E-07 MUEPSP = 5 VTMP=  -1.8463 WVTH0=  -1.01558 MUESR0 = 0.01256478438899837 MUESR1 = 411100000000000\n\
MUEPHW2 = 0 MUEPWP2 = 1\n";
    parameters<const char*> sGrammar;
    const char *pIter = s_ap;
    const char *const pEnd = s_ap + sizeof s_ap - 1;
    PAIRS sValues;
    if (phrase_parse(pIter, pEnd, sGrammar, boost::spirit::ascii::space, sValues) && pIter == pEnd)
    {   std::cerr << "parsing successful!" << std::endl;
        for (const auto &r : sValues)
            std::cout << r.first << "=" << std::scientific << r.second << std::endl;
    }
    else
    {   std::cerr << "parsing failed!" << std::endl;
        std::cerr << pIter << std::endl;
    }
}

1 个答案:

答案 0 :(得分:1)

是的,语法只要求解析严格的实数。如果您不希望这样做,则需要接受其他实数。 @llonesmiz的评论是做到这一点的一种方法。

  

或者,您似乎可以解析双打。尽管二进制实数表示形式可能是“有损的”,但在超过有效位(https://en.wikipedia.org/wiki/Double-precision_floating-point_format)的52/53位之前,尾数的整数部分不会发生这种情况。相比之下,流行的编译器的int为32位。

Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iterator>
#include <iomanip>

namespace qi    = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

typedef boost::variant<int, double> VALUE;
typedef std::pair<std::string, VALUE> PAIR;
typedef std::map<std::string, VALUE> PAIRS;

template <typename Iterator> struct parameters : qi::grammar<Iterator, PAIRS(), ascii::space_type> {

    parameters(void) : parameters::base_type(m_sRoot) {
        m_sName      = qi::lexeme[qi::char_("a-zA-Z_") >> *qi::char_("a-zA-z_0-9")];
        m_sValue     = m_sReal | qi::int_ | qi::double_;
        m_sNameValue = m_sName >> '=' >> m_sValue >> -qi::lit('\n');
        m_sRoot      = m_sNameValue >> *m_sNameValue;

        BOOST_SPIRIT_DEBUG_NODES((m_sName)(m_sValue)(m_sNameValue)(m_sRoot))
    }

  private:
    qi::rule<Iterator,      std::string(), ascii::space_type>  m_sName;
    qi::rule<Iterator,      VALUE(),       ascii::space_type>  m_sValue;
    qi::rule<Iterator,      PAIR(),        ascii::space_type>  m_sNameValue;
    qi::rule<Iterator,      PAIRS(),       ascii::space_type>  m_sRoot;
    qi::real_parser<double, qi::strict_real_policies<double> > m_sReal;
};

int main(int, char **) {
    static const char s_ap[] = R"(
MUEPH1 = 7.014158 MUEPHW=  -0.3 MUEPWP = 0.23 MUEPHL=  -0.72 MUEPLP = 3.4 MUEPHS = 2.976E-07 MUEPSP = 5 VTMP=  -1.8463 WVTH0=  -1.01558 MUESR0 = 0.01256478438899837 MUESR1 = 411100000000000
MUEPHW2 = 0 MUEPWP2 = 1
)";

    parameters<const char*> sGrammar;

    const char *pIter = std::begin(s_ap);
    const char *const pEnd = std::end(s_ap) - 1;
    PAIRS sValues;

    if (phrase_parse(pIter, pEnd, sGrammar, boost::spirit::ascii::space, sValues) && pIter == pEnd) {
        std::cerr << "parsing successful!" << std::endl;
        for (const auto &r : sValues)
            std::cout << r.first << "=" << std::scientific << std::setprecision(2) << r.second << std::endl;
    } else {
        std::cerr << "parsing failed!" << std::endl;
        std::cerr << std::quoted(std::string(pIter, pEnd)) << std::endl;
    }
}

还请注意常规改进,例如将调试添加到规则中。