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

发布于 2025-01-30 19:05:51 字数 2523 浏览 3 评论 0原文

我试图将一系列字符解析为“”,“”,sTD :: map< char,int>对键是字符的对,而值只是分析字符的数量。 例如,如果输入是

 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;
    }
    //]
}

然后,我尝试以这样的方式打印这对:

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

我是初学者,我不知道如何修复此代码。我还想使用字符串而不是字符,因此我输入了一系列由“”隔开的字符串,然后将它们存储在类似于上面提到的类似的地图中。感谢任何帮助!

I am trying to parse a sequence of characters separated by a "," into an std::map<char,int> of pairs where the key is the character and the value just the a count of parsed characters.
For example, if the input is

 a,b,c

The map should contain the pairs:

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

Here's the code I am using :

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;
    }
    //]
}

Then I try to print the pair in main like this:

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

I am a beginner and I don't know how to fix this code . I also want to use strings instead of characters so I enter a sequence of strings separated by a "," and store them in a map similar to the one mentioned above. I would appreciate any help !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

娇纵 2025-02-06 19:05:52

您不能在凤凰城延期演员之外使用凤凰座持有人。例如,std :: make_pair的类型(qi :: _ _ 1,0) is std :: pair&lt&lt&lt; boost :: Phoenix :: Actor&lt; boost :: Phoenix :: phoenix :: gright&lt; lt; 0&gt; gt; &gt;,int&gt;

没有什么可以与这样的事情互动。当然不是 std :: map&lt; :: insert

您需要做的是将所有操作作为凤凰演员作为语义动作。

#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您可以:

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

live noreferrer“> live C ++ 17:

live 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 事情,所以,也许使用

noreflow noreferrer“> live < /a>

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&gt;&gt; *(',','&gt;&gt; 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 :: Space :: Spaceqi :: ascii :: Space上面)。

You cannot use Phoenix place holders outside Phoenix deferred actors. E.g. the type of std::make_pair(qi::_1, 0) is std::pair<boost::phoenix::actor<boost::phoenix::argument<0>>, int>.

Nothing interoperates with such a thing. Certainly not std::map<>::insert.

What you'd need to do is wrap all the operations in semantic actions as Phoenix actors.

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

Then you can:

#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

See it Live

Easy peasy. Right.

I spent half an hour on that thing debugging why it wouldn't work. Why is this so hard?

It's because someone invented a whole meta-DSL to write "normal C++" but with defferred execution. Back when that happened it was pretty neat, but it is the mother of all leaky abstractions, with razor sharp edges.

So, what's new? Using C++11 you could:

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

Or using 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;
}

On a tangent, you probably wanted to count things, so, maybe use

Live

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

By this time, you could switch to Spirit X3 (which requires 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

Now finally, let's simplify. p >> *(',' >> p) is just a clumsy way of saying 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);
}

And you wanted words, not characters:

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";
        }
    }
}

Prints

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

Note the behaviour of the x3::space (like qi::space and qi::ascii::space above).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文