boost spirit qi numeric parsing of integer and flo

2019-01-24 14:51发布

i am trying to make sense of the following result. The test case code is

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/spirit/home/support/context.hpp>
#include <boost/spirit/home/phoenix.hpp>
#include <boost/foreach.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>

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

namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;

using phoenix::at_c;
using phoenix::push_back;
using phoenix::bind;

template <typename P>
void test_parser(
    char const* input, P const& p, bool full_match = true)
{
    using boost::spirit::qi::parse;

    char const* f(input);
    char const* l(f + strlen(f));
    if (parse(f, l, p) && (!full_match || (f == l)))
        std::cout << "ok" << std::endl;
    else
        std::cout << "fail" << std::endl;
}


int main() {

test_parser("+12345", qi::int_ ); //Ok
test_parser("+12345", qi::double_ - qi::int_ ); //failed, as expected
test_parser("+12345.34", qi::int_ );  // failed, as expected
test_parser("+12345.34", qi::double_ - qi::int_ );  //failed but it should be Ok!
};

the motivation here is that i want to match numbers '12345' as integers and NEVER as floating points. '12345.34' will match double_ and never int_ but the reciprocal case is not true; '12345' matches both integers (int_ ) and floating point (double_ ). I tried double_ - int_ and it successfully failed to match '12345'. However my hope was that the last test case '12345.34' would positively match double_ - int_, but the result i get is fail to match.

Why this is so, and how do i get a parser that only matches integers and another that only matches floating points (like in c, 5.0 would be interpreted as floating point)

2条回答
狗以群分
2楼-- · 2019-01-24 15:35

The problem with the double-not-int qi::double_ - qi::int_ is that an individual parser doesn't have to match the whole input to be a successful match. For "+12345.34", qi::double_ makes a successful match on the whole thing and qi::int makes a successful match on "+12345", so that qi::double_ - qi::int_ is a non-match. For difference operator, think about applying each parser separately and whether there is a valid match for each for even the first part of the input.

You can get the behavior you want by requiring some kind of boundary after qi::int_. What follows when an qi::int_ matches the first part of a float is a valid float (e.g. qi::int_ on "+12345.34" matches "+12345", leaving ".34" next on the stream). Therefore, you can do a negative look ahead for a float:

int_rule %= qi::int_ >> !qi::double_;

double_rule %= qi::double_ - int_rule; 

or

double_rule %= qi::double_ - (qi::int_ >> !qi::double_); 

!qi::double is also true for whitespace and eoi, so I think this should be pretty general for standard format. This won't work for scientific notation though.

查看更多
劫难
3楼-- · 2019-01-24 15:47

For your specific example, I think it's actually described in the Boost Spirit documentation under RealPolicies Specialization. To make things a bit easier for you, I whipped out a quick "real" parser, that only parses real numbers and not integers(or at least it worked with your simplified examples):

template <typename T>
struct strict_real_policies : qi::real_policies<T>
{
    static bool const expect_dot = true;
};

qi::real_parser< double, strict_real_policies<double> > real;

And you can use this just like any other parser(like int_ and double_). You might have to add:

#include <boost/spirit/include/qi_numeric.hpp>

To get it to compile.

查看更多
登录 后发表回答