c++ - Parsing a list of doubles with boost::spirit::qi -
i'm getting familiar boost::spirit , want parse strings below:
double_1 | double_2 | ... | double_n | double_1% | double_2% ... | double_m%
where m>=0, n>=0.
for example, lines below should parse ok:
91.3 | 44 | 5e-3 | 12% | 11%
91.3 | 44 | 5e-3
12% | 11%
i want use boost::spirit::qi
.
so, i've written 2 parsers below:
namespace client { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; template <typename iterator> bool parse_numbers(iterator& first, iterator last, std::vector<double>& v) { using qi::double_; using qi::phrase_parse; using ascii::space; bool r = phrase_parse(first, last, // begin grammar ( // double_ % '|' double_ >> *('|' >> double_ >> '|') ) , // end grammar space, v); return r; } template <typename iterator> bool parse_numberswithpercents(iterator& first, iterator last, std::vector<double>& v) { using qi::double_; using qi::phrase_parse; using ascii::space; bool r = phrase_parse(first, last, // begin grammar ( (double_ >> '%') % '|' ) , // end grammar space, v); if (first != last) // fail if did not full match return false; return r; } }
and, then, i'm calling them in main below:
int main() { std::cout << "give me list of numbers in format double_1 | double_2 | ... | double_n | double_1% | double_2% ... | double_m%\n"; std::cout << "the numbers inserted in vector of numbers\n"; std::cout << "type [q or q] quit\n\n"; std::string str; while (getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'q') break; std::vector<double> v; std::string::iterator begin = str.begin(), end = str.end(); if (client::parse_numbers(begin, end, v)) { std::cout << "-------------------------\n"; std::cout << "first part parsing succeeded\n"; (std::vector<double>::size_type = 0; < v.size(); ++i) std::cout << << ": " << v[i] << std::endl; std::cout << "\n-------------------------\n"; if(begin != end) { if('|' == *begin) ++begin; if(begin != end) { std::cout << "parsing second part: " << std::string(begin, end) << std::endl; std::vector<double> v1; if (client::parse_numberswithpercents(begin, end, v1)) { std::cout << "-------------------------\n"; std::cout << "second part parsing succeeded\n"; (std::vector<double>::size_type = 0; < v1.size(); ++i) std::cout << << ": " << v1[i] << std::endl; std::cout << "\n-------------------------\n"; } else { std::cout << "-------------------------\n"; std::cout << "second part parsing failed\n"; std::cout << "-------------------------\n"; if(begin != end) { std::cout << "remaining part is: " << std::string(begin, end) << std::endl; } } } } } else { std::cout << "-------------------------\n"; std::cout << "first part parsing failed\n"; std::cout << "-------------------------\n"; if(begin != end) { std::cout << "remaining part is: " << std::string(begin, end) << std::endl; } } } std::cout << "bye... :-) \n\n"; return 0; }
as can see, method doesn't work correct corner cases like:
91.3 | 44 | 5e-3
12% | 11%
i'm interested there way same in more simple way using boost library. or somehow correct parsers right parsing of above corner cases. nice have first , second parts in separate containers.
thanks in advance.
hah. intuition should enormously simple. however, i've come conclude indeed it's bit nontrivial.
the problem making non-list-repeat separator optional. thought long , hard elegant way make optional , came this:
#include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; namespace { using double_vec = std::vector<double>; using = std::string::const_iterator; static const qi::rule<it, double_vec(bool percent), qi::blank_type> doubles_ = (qi::double_ >> (qi::eps(qi::_r1) >> '%' | !qi::lit('%'))) % '|'; } int main() { std::string str; while (std::getline(std::cin, str)) { f = str.begin(), l = str.end(); double_vec v, w; bool ok = qi::phrase_parse(f, l, (doubles_(false) >> -('|' >> doubles_(true))) | qi::attr(double_vec{}) >> doubles_(true), qi::blank, v, w); if (ok && f == l) { std::cout << "parsed " << v.size() << "/" << w.size() << " elements\n"; } else { std::istringstream iss(str); if (iss >> str && (str == "q" || str == "q")) break; std::cout << "invalid input. remaining '" << std::string(f,l) << "'\n"; } } }
which produces following result given test inputs:
./test <<input 91.3 | 44 | 5e-3 | 12% | 11% 91.3 | 44 | 5e-3 12% | 11% q input parsed 3/2 elements parsed 3/0 elements parsed 0/2 elements
depending on you're trying /actually/ achieve here, things more elegant
update in response comments, here's how i'd improve relaxing grammar. note how shift ignoring '|' skipper:
qi::phrase_parse( f, l, *(qi::double_>>!qi::lit('%')) >> *(qi::double_>>'%'), qi::blank | '|', v, w);