使用 boost::qi 将字符解析为 std::map<char,int>

Parsing characters into an std::map<char,int> using boost::qi

我正在尝试将由“,”分隔的字符序列解析为 std::map 对,其中键是字符,值只是已解析字符的计数. 例如,如果输入是

 a,b,c

地图应包含对:

(a,1) , (b,2) , (c,3) 

这是我使用的代码:

namespace myparser
{
    std::map<int, std::string> mapping;
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;
    int i = 0;

    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, std::map<char,int>& v)
    {
        using qi::double_;
        using qi::char_;
        using qi::phrase_parse;
        using qi::_1;
        using ascii::space;
        using phoenix::push_back;

        bool r = phrase_parse(first, last,

            //  Begin grammar
            (

               
                char_[v.insert(std::make_pair(_1,0)]
                    >> *(',' >> char_[v.insert(std::make_pair(_1,0)])
            )
            ,
            //  End grammar

            space);

        if (first != last) // fail if we did not get a full match
            return false;
        return r;
    }
    //]
}

然后我尝试像这样在 main 中打印这对:

int main() {
  std::string str;
    while (getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        std::map<char,int> v;
        std::map<std::string, int>::iterator it = v.begin();
        if (myparser::parse_numbers(str.begin(), str.end(), v))
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << str << " Parses OK: " << std::endl;

        while (it != v.end())
        {
        // Accessing KEY from element pointed by it.
        std::string word = it->first;
        // Accessing VALUE from element pointed by it.
        int count = it->second;
        std::cout << word << " :: " << count << std::endl;
        // Increment the Iterator to point to next entry
        it++;
         }

            std::cout << "\n-------------------------\n";
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }
return 0;
}

我是初学者,我不知道如何修复这段代码。我还想使用字符串而不是字符,因此我输入了一系列由“,”分隔的字符串,并将它们存储在类似于上述映射的映射中。如果有任何帮助,我将不胜感激!

您不能在 Phoenix 延迟演员之外使用 Phoenix 占位符。例如。 std::make_pair(qi::_1, 0) 的类型是 std::pair<boost::phoenix::actor<boost::phoenix::argument<0>>, int>.

没有任何东西可以与这样的东西互操作。当然不是 std::map<>::insert.

您需要做的是将所有操作包装在语义动作中作为 Phoenix actors。

#include <boost/phoenix.hpp>
namespace px = boost::phoenix;

那么您可以:

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

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

namespace myparser {
    using Map = std::map<char, int>;

    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, Map& m) {
        auto action = px::insert(px::ref(m), px::end(px::ref(m)),
                                 px::construct<std::pair<char, int>>(qi::_1, 0));

        bool r = qi::phrase_parse( //
            first, last,
            //  Begin grammar
            qi::char_[action] >> *(',' >> qi::char_[action]),
            //  End grammar
            qi::space);

        return r && first == last;
    }
} // namespace myparser

看到了Live

简单易行。对。

我花了半个小时在那件事上调试为什么它不能工作。为什么这么难?

这是因为有人发明了一个完整的 meta-DSL 来编写“普通 C++”但延迟执行。当那件事发生时,它非常整洁,但它是所有漏洞抽象之母,边缘锋利。

那么,有什么新鲜事吗?使用 C++11,您可以:

Live

template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
    struct action_f {
        Map& m_;
        void operator()(char ch) const { m_.emplace(ch, 0); }
    };
    px::function<action_f> action{{m}};

    bool r = qi::phrase_parse( //
        first, last,
        //  Begin grammar
        qi::char_[action(qi::_1)] >> *(',' >> qi::char_[action(qi::_1)]),
        //  End grammar
        qi::space);

    return r && first == last;
}

或者使用 c++17:

Live

template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
    px::function action{[&m](char ch) { m.emplace(ch, 0); }};

    bool r = qi::phrase_parse( //
        first, last,
        //  Begin grammar
        qi::char_[action(qi::_1)] >> *(',' >> qi::char_[action(qi::_1)]),
        //  End grammar
        qi::space);

    return r && first == last;
}

在切线上,您可能想要 count 个东西,所以,也许使用

Live

px::function action{[&m](char ch) { m[ch] += 1; }};

此时,您可以切换到 Spirit X3(需要 C++14):

Live

#include <boost/spirit/home/x3.hpp>
#include <map>

namespace x3 = boost::spirit::x3;

namespace myparser {
    using Map = std::map<char, int>;

    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, Map& m) {
        auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };

        return x3::phrase_parse( //
                first, last,
                //  Begin grammar
                x3::char_[action] >> *(',' >> x3::char_[action]) >> x3::eoi,
                //  End grammar
                x3::space);
    }
} // namespace myparser

最后,让我们简化一下。 p >> *(',' >> p) 只是 p % ',':

的笨拙表达方式

Live

template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
    auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };

    return x3::phrase_parse(     //
        first, last,             //
        x3::char_[action] % ',', //
        x3::space);
}

而你想要的是文字,而不是字符:

Live

#include <boost/spirit/home/x3.hpp>
#include <map>

namespace x3 = boost::spirit::x3;

namespace myparser {
    using Map = std::map<std::string, int>;

    template <typename Iterator>
    bool parse_numbers(Iterator first, Iterator last, Map& m) {
        auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };

        auto word_ = (*~x3::char_(','))[action];

        return phrase_parse(first, last, word_ % ',', x3::space);
    }
} // namespace myparser

#include <iomanip>
#include <iostream>

int main() {
    for (std::string const str : {"foo,c++ is strange,bar,qux,foo,c++       is strange   ,cuz"}) {
        std::map<std::string, int> m;

        std::cout << "Parsing " << std::quoted(str) << std::endl;

        if (myparser::parse_numbers(str.begin(), str.end(), m)) {
            std::cout << m.size() << " words:\n";
            for (auto& [word,count]: m)
                std::cout << " - " << std::quoted(word) << " :: " << count << std::endl;
        } else {
            std::cerr << "Parsing failed\n";
        }
    }
}

版画

Parsing "foo,c++ is strange,bar,qux,foo,c++       is strange   ,cuz"
5 words:
 - "bar" :: 1
 - "c++isstrange" :: 2
 - "cuz" :: 1
 - "foo" :: 2
 - "qux" :: 1

注意 x3::space 的行为(如上面的 qi::spaceqi::ascii::space)。