Search code examples
boost-spirit-qi

Parsing Lat/Long failed: no character 'E'


I want to parse a lot of Lat/Long coordinates with the following format 1.123456W or 50.123456N, basically a double followed by a char ('N', 'S', 'W', 'E'). I just want to remove the character from the string, convert to double and change the sign if its W-est or S-outh. The following code works in three out of four cases:

This works for 1.123456W, or 50.123456N, or 9.123456S, but not for 7.123456E. I'm guessing the Qi parser expects an E at the input string to correlate with an exponent representation of a double and fails because its incomplete? But how do I tell Qi to skip the exponent if it fails an just decodes the string to the point of where the E resides?

double d;
char c;
auto const expr = boost::spirit::qi::double_ >> boost::spirit::qi::char_;
std::string tok = "7.123456E";
bool success = boost::spirit::qi::parse( tok.cbegin(), tok.cend(), expr, d, c ) ) {
if( success ) {
    if( c == 'W' || c == 'w' || c == 'S' || c == 'S' ) {
        d = -d;
    }
    /// ....
}

Thank you very much!


Solution

  • Here you go. The Coliru compiler didn't like your use of auto, so I made a minimal change to fix it.

    Have a look at the RealPolicies documentation I supplied above. You create a new policy class based on the ureal_policies template, override the exponent-related methods and return false from them.

    #include <iostream>
    #include <boost/spirit/include/qi.hpp>
    
    using namespace std;
    
    //create a real number parser policy that doesn't handle exponent notation
    template <typename T>
    struct no_exp_policy : boost::spirit::qi::ureal_policies<T>
    {
        //  No exponent
        template <typename Iterator>
        static bool
        parse_exp(Iterator&, Iterator const&)
        {
            return false;
        }
    
        //  No exponent
        template <typename Iterator, typename Attribute>
        static bool
        parse_exp_n(Iterator&, Iterator const&, Attribute&)
        {
            return false;
        }
    };
    
    int main(int argc, char **argv) {
       double d;
       char c;
       // no_exp is a real number parser that ignores exponents and has a double attribute
       boost::spirit::qi::real_parser<double, no_exp_policy<double> > no_exp;
       std::string tok = "7.123456E";
       bool success = boost::spirit::qi::parse( tok.cbegin(), tok.cend(), 
        no_exp >> boost::spirit::qi::char_("NESWnesw"), 
        d, c );
       if( success ) {
           if( c == 'W' || c == 'w' || c == 'S' || c == 's' ) {
               d = -d;
           }
           cout << d << " " << c << endl;
       }
       else
           cout << "failed" << endl;
    
       return 0;
    }
    

    Output:

    7.12346 E
    

    Hope this helps, though I agree it's a bit long-winded. I'm still not 100% sure it's not a spirit error that 7.12346E is handled by the default real parser as a real number, as IMHO you need to have an exponent value after the E to make it valid.