2010-06-26 4 views
5

Ich versuche, das folgende Ergebnis zu verstehen. Der Testfall-Code istboost spirit qi numerisches syntaktisches Parsing von Integer- und Gleitkommazahlen

#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! 
}; 

die Motivation hier ist, dass ich Zahlen übereinstimmen ‚12345‘ als ganze Zahlen will und nie als floating Punkte. '12345.34' wird double_ und niemals int_ entsprechen, aber der umgekehrte Fall ist nicht wahr; '12345' passt sowohl zu ganzen Zahlen (int_) als auch zu Fließkommazahlen (double_). Ich habe versucht double_ - int_ und es konnte nicht erfolgreich '12345' übereinstimmen. Ich hoffe jedoch, dass der letzte Testfall '12345.34' positiv mit double_ - int_ übereinstimmt, aber das Ergebnis, das ich bekomme, stimmt nicht überein.

Warum dies so ist, und wie bekomme ich einen Parser, der nur ganze Zahlen und anderen Spiele, die nur Gleitpunkte entsprechen (wie in c, würde 5,0 als Fließkomma interpretiert werden)

Antwort

14

Für Ihr spezifisches Beispiel, ich denke, es ist tatsächlich in der unter RealPolicies Spezialisierung beschrieben. Um die Dinge ein bisschen leichter zu machen, Schlag ich einen schnellen „echten“ Parser aus, dass nur reelle Zahlen und nicht die ganzen Zahlen (oder zumindest es funktionierte mit vereinfachten Beispiele) analysiert:

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; 

Und Sie können verwenden Dies ist wie jeder andere Parser (wie int_ und double_). Möglicherweise müssen Sie hinzufügen:

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

Um es zum Kompilieren zu bekommen.

+0

+1 Dies ist eine bessere Lösung. – academicRobot

+0

danke, das funktioniert perfekt – lurscher

4

Das Problem mit der doppel- not-int qi::double_ - qi::int_ ist, dass ein einzelner Parser nicht mit der gesamten Eingabe übereinstimmen muss, um erfolgreich zu sein. Für "+12345.34" macht qi :: double_ eine erfolgreiche Übereinstimmung für das Ganze und qi :: int macht eine erfolgreiche Übereinstimmung auf "+12345", so dass qi::double_ - qi::int_ nicht übereinstimmt. Denken Sie für den Differenzoperator darüber nach, ob Sie jeden Parser einzeln anwenden und ob für jeden einzelnen Parser sogar für den ersten Teil der Eingabe eine gültige Übereinstimmung besteht.

Sie können das gewünschte Verhalten erhalten, indem Sie eine Art Grenze nach qi :: int_ benötigen. Was folgt, wenn ein Qi :: Int_ mit dem ersten Teil eines Floats übereinstimmt, ist ein gültiger Float (z. B. entspricht qi :: int_ auf "+12345.34" "+12345", so dass als nächstes ".34" im Stream übrig bleibt). Daher können Sie einen negativen Vorgriff für einen Schwimmer tun:

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

double_rule %= qi::double_ - int_rule; 

oder

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

!qi::double gelten auch für Leerzeichen und eoi, so denke ich, soll dies für Standard-Format recht allgemein sein. Dies funktioniert jedoch nicht für die wissenschaftliche Notation.

+0

ah Ich dachte, der A - B - Operator handelte wie folgt: gegeben eine Zeichenfolge N, die A entspricht, wenn B es entspricht dann AB wird nicht übereinstimmen (aber hier ist die Sache, die Sie erwähnen, es kann _part_ davon mit B übereinstimmen, nicht die ganze Sache) also ist nicht sehr intuitiv, aber ich verstehe jetzt, danke! – lurscher