2013-09-02 3 views
3

В настоящее время я пытаюсь выполнить некоторую работу с использованием boost::spirit::qi::phrase_parse, но я не способный понять это сам.Сохранять значения в std :: map <std :: string, std :: string> using boost :: spirit :: qi :: phrase_parse

Стоит упомянуть: я совершенно новичок в повышении и, таким образом, для повышения: дух.

Я получаю вход формы "{A [B C] -> F [D E], C ->E,B->Z}"

Я хотел бы разобрать этот тип входа в std::map<std::string, std::string>. Ключ должен содержать каждые std::string до "->" и значение каждые std::string после "->" до тех пор, пока не произойдет ','.

Кроме того, не следует хранить '[' и ']'.

Так содержание std::map должно быть что-то вроде этого после того, как синтаксический удалось:

 { 
     ("A", "F"), 
     ("A", "D E"), 
     ("B C", "F"), 
     ("B C", "D E"), 
     ("C", "E"), 
     ("B", "Z") 
    } 

Мой первый подход был хранить все ключи/значения в std::vector<std::string>.

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

    #include <iostream> 
    #include <string> 
    #include <vector> 

    int main() 
    { 
     using boost::spirit::qi::phrase_parse; 
     using boost::spirit::qi::char_; 
     using boost::spirit::qi::lexeme; 

     std::string input = "{A [B C] -> F [D E], C ->E,B->Z}"; 
     std::string::const_iterator beg = input.begin(), end = input.end(); 

     std::vector<std::string> sdvec; 

     bool r = phrase_parse( beg, 
           end, 
           '{' >> (+(+char_("a-zA-Z0-9") | lexeme[('[' >> +char_("a-zA-Z0-9 ") >> ']')]) >> '-' >> '>' >> +(+char_("a-zA-Z0-9") | lexeme[('[' >> +char_("a-zA-Z0-9 ") >> ']')])) % ',' >> '}', 
           boost::spirit::ascii::space, 
           sdvec 
          ); 

     if(beg != end) { 
      std::cout << "Parsing failed!" << std::endl; 
     } else { 
      std::cout << "Parsing succeeded!" << std::endl;  
     } 

     for(int i=0; i<sdvec.size(); i++) { 
      std::cout << i << ": " << sdvec[i] << std::endl; 
     } 

     return 0; 
    } 

Выполнение этого я получаю каждый нашел std::string как запись в std::vector:

Parsing 2 succeeded! 
    0: A 
    1: B C 
    2: F 
    3: D E 
    4: C 
    5: E 
    6: B 
    7: Z 

Но я не имею ни малейшего представления, как анализировать эти значения в std::map<std::string, std::string> используя boost::spirit::qi::phrase_parse просто как замена бросков некоторые компиляционные ошибки.

EDIT:

На самом деле я нашел то, что это совсем то, что мне нужно: http://boost-spirit.com/home/articles/qi-example/parsing-a-list-of-key-value-pairs-using-spirit-qi/

Я принял код этой статьи в соответствии с моей проблемой:

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

    #include <iostream> 
    #include <string> 
    #include <vector> 
    #include <map> 

    namespace qi = boost::spirit::qi; 

    template <typename Iterator> 
    struct keys_and_values 
     : qi::grammar<Iterator, std::map<std::string, std::string>()> 
    { 
     keys_and_values() 
      : keys_and_values::base_type(query) 
     { 
      query = '{' >> *qi::lit(' ') >> pair >> *(qi::lit(',') >> *qi::lit(' ') >> pair) >> *qi::lit(' ') >> '}'; 
      pair = key >> -(*qi::lit(' ') >> "->" >> *qi::lit(' ') >> value); 
      key = +qi::char_("a-zA-Z0-9") | qi::lexeme[('[' >> +qi::char_("a-zA-Z0-9 ") >> ']')]; 
      value = +qi::char_("a-zA-Z0-9") | qi::lexeme[('[' >> +qi::char_("a-zA-Z0-9 ") >> ']')]; 
     } 
     qi::rule<Iterator, std::map<std::string, std::string>()> query; 
     qi::rule<Iterator, std::pair<std::string, std::string>()> pair; 
     qi::rule<Iterator, std::string()> key, value; 
    }; 

    int main() 
    { 
     std::string input = "{AB -> CD, E -> F, G -> HI, [J K L] -> [M N O]     }"; 

     std::string::iterator begin = input.begin(); 
     std::string::iterator end = input.end(); 

     keys_and_values<std::string::iterator> p; // create instance of parser 
     std::map<std::string, std::string> m;  // map to receive results 
     bool result = qi::phrase_parse(begin, end, p, boost::spirit::ascii::space, m); // returns true if successful 

     if(begin != end) { 
      std::cout << "Parsing failed!" << std::endl; 
     } else { 
      std::cout << "Parsing succeeded!" << std::endl;  
     } 

     std::cout << m["AB"] << "\t" << m["E"] << "\t" << m["G"] << "\t" << m["J K L"] << std::endl; 

     return 0; 
    } 

В результате этого более или менее то, что мне нужно:

Parsing succeeded! 
CD F HI M N O 

Моей последней проблемой для решения является такой случай, как A [B C] -> F [D E].

Любой способ получить эти четыре пары ключей с ключом ("A", "F"), ("A", "D E"), ("B C", "F"), ("B C", "D E") в мой std::map<std::string, std::string> m?

Или, может быть, проще разобрать его в std::map<std::vector<std::string>, std::vector<std::string> >, где каждый std::vector<std::string> содержит все ключи/значения?

Например:

in: "{A [B C] -> F [D E], C ->E,B->Z}" 
out: { ({"A", "B C"}, {"F", "D E"}), ({"C"}, {"E"}), ({"B"}, {"Z"}) } 

Спасибо за любую помощь!

ответ

4

Я думаю, что вы очень близки к своей цели, так что я пропущу комбинаторную часть :-) Анализатор будет делать то, что он должен сделать ... чтобы проверить синтаксис и разметить данные затем он передает ключи, значение и выход карты (MultiMap) аргументы в функцию феникса inserter где вы можете вставить whatever вам нужно в вашей карте (Multimap)

#if __cplusplus >= 201103L 
#define BOOST_RESULT_OF_USE_DECLTYPE 
#endif 
#define BOOST_SPIRIT_USE_PHOENIX_V3 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <iomanip> 
#include <vector> 
#include <map> 

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

typedef std::map< std::string,std::string > TMap; 
//typedef std::multimap< std::string,std::string > TMap; 

struct SMapInsert 
{ 
    template <typename Arg1,typename Arg2,typename Arg3> 
    struct result 
    { 
     typedef void type; 
    }; 

    template <typename Arg1,typename Arg2,typename Arg3> 
    void operator()(Arg1&out, Arg2&keys, Arg3&vals) const 
    { 
     std::cout << "Keys:" << std::endl; 
     for(const auto &key : keys) 
      std::cout << std::left << "`" << key << "`" << std::endl; 
     std::cout << "Vals:" << std::endl; 
     for(const auto &val : vals) 
      std::cout << std::left << "`" << val << "`" << std::endl; 
     // your map here... 
     // out.insert 
    } 
}; 

boost::phoenix::function<SMapInsert> inserter; 

int main() 
{ 
    std::string input = "{A [B C] -> F [D E], C ->E,B->Z}"; 
    TMap data; 

    std::string::const_iterator iter = input.begin(); 
    std::string::const_iterator last = input.end(); 

    qi::rule< std::string::const_iterator,std::string() > token=+qi::alnum; 
    qi::rule< std::string::const_iterator,ascii::space_type,std::vector<std::string>() > 
     keyOrvalue = +(token | ('[' >> qi::lexeme[ +qi::char_("a-zA-Z0-9 ") ] >> ']')); 
    qi::rule< std::string::const_iterator,ascii::space_type, TMap() > 
     root = '{' >> ((keyOrvalue >> "->" >> keyOrvalue)[ inserter(qi::_val, qi::_1, qi::_2) ]) % ',' >> '}'; 
    std::cout << "input: `" << input << "`" << std::endl; 
    if(qi::phrase_parse(iter, last, root, ascii::space, data) && iter==last) 
    { 
     for(const auto &keyValue : data) 
      std::cout << std::left << std::setw(10) << keyValue.first << std::setw(10) << keyValue.second << std::endl; 
    } 
    else 
     std::cout << "parsing failed:" << std::string(iter,last) << std::endl;   

    return 0; 
} 
+1

Мне не очень нравится использование семантических действий (я глупый в этом отношении), но это решение намного яснее, чем мое. – llonesmiz

+0

@cv_and_he Я также стараюсь избегать семантических действий ... но иногда они упрощают вещи. –

+0

Ясно, что они делают :). – llonesmiz

3

Редактировать: Это альтернативный способ сделать это, но я думаю, что это намного менее понятно, чем решение Г. Чиварди.

Как вы заметили, синтаксический анализ на map<vector<string>,vector<string>> был бы самым простым способом, и вы можете позже управлять им, чтобы получить карту, которую вы действительно хотите. В приведенном ниже решении используется промежуточная структура (в основном эквивалентная map<vector,vector>), а затем используется точка настройки transform_attribute, чтобы заполнить мультимап (так как есть клавиши, которые повторяются).
PS: Прошу простить использование диапазонов для циклов, измените их, если вы не можете использовать C++ 11.

Running on coliru.

#define BOOST_SPIRIT_DEBUG 

#include <boost/spirit/include/qi.hpp> 
#include <boost/fusion/include/adapt_struct.hpp> 

#include <iostream> 
#include <string> 
#include <vector> 
#include <map> 

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

struct key_value 
{ 
    std::vector<std::string> keys; 
    std::vector<std::string> values; 
}; 

struct intermediate_struct 
{ 
    std::vector<key_value> data; 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    key_value, 
    (std::vector<std::string>, keys) 
    (std::vector<std::string>, values) 
) 

BOOST_FUSION_ADAPT_STRUCT(
    intermediate_struct, 
    (std::vector<key_value>, data) 
) 

namespace boost{ namespace spirit{ namespace traits 
{ 

    template <> 
    struct transform_attribute<std::multimap<std::string,std::string>,intermediate_struct,qi::domain> 
    { 
     typedef intermediate_struct type; 

     static type pre(std::multimap<std::string,std::string>&) 
     { 
      return intermediate_struct(); 
     } 
     static void post(std::multimap<std::string,std::string>& map, intermediate_struct const& intermediate) 
     { 
      for(const auto& key_val : intermediate.data) 
      { 
       for(const auto& key : key_val.keys) 
       { 
        for(const auto& val : key_val.values) 
        { 
         map.insert(typename std::multimap<std::string,std::string>::value_type(key,val)); 
        } 
       } 
      } 
     } 
     static void fail(std::multimap<std::string,std::string>&){} 
    }; 

}}} 

int main() 
{ 
    std::string input = "{A [B C] -> F [D E], C ->E,B->Z}"; 
    std::string::const_iterator iter = input.begin(), end = input.end(); 

    std::multimap<std::string,std::string> sdmap; 

    qi::rule<std::string::const_iterator,std::string(),ascii::space_type> text_rule = 
     +qi::char_("a-zA-Z0-9") | qi::lexeme[('[' >> +qi::char_("a-zA-Z0-9 ") >> ']')]; 
    qi::rule<std::string::const_iterator,std::vector<std::string>(),ascii::space_type> keys_rule = 
     +text_rule; 
    qi::rule<std::string::const_iterator,std::vector<std::string>(),ascii::space_type> values_rule = 
     +text_rule; 
    qi::rule<std::string::const_iterator,intermediate_struct(),ascii::space_type> map_rule = 
     qi::eps >> ('{' >> (keys_rule >> "->" >> values_rule)%',' >> '}'); 

    BOOST_SPIRIT_DEBUG_NODES((map_rule)(keys_rule)(values_rule)); 

    bool r = qi::phrase_parse( iter, 
          end, 
          map_rule, 
          ascii::space, 
          sdmap 
         ); 

    if(r && (iter == end)) { 
     std::cout << "Parsing succeeded!" << std::endl; 
     for(const auto& pair : sdmap) { 
      std::cout << pair.first << ": " << pair.second << std::endl; 
     } 
    } else { 
     std::cout << "Parsing Failed!" << std::endl; 
     std::cout << "Unparsed: " << std::string(iter,end) << std::endl; 
    } 

    return 0; 
} 
+0

эй это было туго. Cheers :-) –