boost :: spirit и strict_real_policies не в состоянии проанализировать слишком длинное целое число - PullRequest
0 голосов
/ 20 ноября 2018

Мне приходится иметь дело с действительно длинными целыми числами в текстовом формате - настолько длинными, что они не помещаются в 32-битное целое число.Мне нужно разобрать такой текст в

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 голосов
/ 21 ноября 2018

Да, грамматика требует анализа ТОЛЬКО строгих действительных значений.Если вы не хотите этого, вам нужно принять другие предложения.Комментарий @ llonesmiz - один из способов сделать это.

Кроме того, кажется, что вы можете просто разобрать удваивается.Хотя двоичное реальное представление может быть «потерянным», что не может произойти для неотъемлемой части мантиссы, пока вы не превысите 52/53 битов значимости и (https://en.wikipedia.org/wiki/Double-precision_floating-point_format). Для сравнения, популярные компиляторы имеют 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;
    }
}

Обратите внимание также на общие улучшения, такие как добавление отладки втвои правила.

...