使用Boost.spirit解析一个简单的重复文本宏

时间:2013-08-22 10:04:11

标签: c++ parsing boost-spirit-qi

我正在学习如何使用Boost.Spirit库来解析字符串。它似乎是一个非常好的工具,但也很难。所以,我想用一些用/分隔的单词解析一个字符串,并将它们放在一个字符串向量中。以下是一个示例:word1/word2/word3。这是一项简单的任务,我可以通过以下方式完成此任务:

bool r = phrase_parse(first, last, (+~char_("/") % qi::lit("/")),space,v)

其中vstd::vector<std::string>。但总的来说,我想解析w1/[w2/w3]2/w4等同于w1/w2/w3/w2/w3/w4的内容,即[w2/w3]2意味着w2/w3重复两次。有人能给我一些想法吗?我读了documentation但仍有一些问题。

提前谢谢!

2 个答案:

答案 0 :(得分:3)

完全正常工作的演示: live on Coliru

如果状态为raw,那么通过简单方法添加的]值可选择在in_group结束。

我选择使用继承的属性bool)传递状态。

此实现也允许嵌套子组,例如:"[w1/[w2/w3]2/w4]3"

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

namespace phx = boost::phoenix;

int main()
{
    typedef std::string::const_iterator It;
    const std::string input = "[w1/[w2/w3]2/w4]3";

    std::vector<std::string> v;
    It first(input.begin()), last(input.end());

    using namespace boost::spirit::qi;

    rule<It, std::string(bool in_group)> raw;
    rule<It, std::vector<std::string>(bool in_group), space_type> 
        group, 
        delimited;

    _r1_type in_group; // friendly alias for the inherited attribute

    raw       = eps(in_group) >> +~char_("/]") 
              | +~char_("/");

    delimited = (group(in_group)|raw(in_group)) % '/';

    group     = ('[' >> delimited(in_group=true) >> ']' >> int_) 
        [ phx::while_(_2--) 
            [ phx::insert(_val, phx::end(_val), phx::begin(_1), phx::end(_1)) ]
        ];

    BOOST_SPIRIT_DEBUG_NODES((raw)(delimited)(group));

    bool r = phrase_parse(first, last, 
            delimited(false),
            space,v);

    if (r)
        std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}

打印:

w1
w2
w3
w2
w3
w4
w1
w2
w3
w2
w3
w4
w1
w2
w3
w2
w3
w4

(除了调试信息)

答案 1 :(得分:2)

这是我的快速实现(c ++ 11)。您可以在boost-spirit-qi中找到解决各种问题的大量场景,我同意学习SPIRIT需要付出一些努力: - )

#define BOOST_RESULT_OF_USE_DECLTYPE
#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 <string>

struct SInsert
{
    struct result
    {
        typedef void type;
    };

    void operator()( std::vector<std::string>&out, 
                     std::vector<std::string>&in, int counter ) const
    {
        for( int i=0; i<counter; ++i )
            std::copy( in.begin(), in.end(), std::back_inserter(out) );
    }
};

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

int main()
{
    namespace qi = boost::spirit::qi;
    namespace ph = boost::phoenix;
    namespace ascii = boost::spirit::ascii;

    for ( auto &str : std::vector< std::string >
        {   "w1/ w2 /w4 ",
            "[w2]1 /w4 ",
            "[w2/w3]2 /w4 ",
            "[]0",
            "[]0 / w4"
        }
    )
    {
        std::cout << "input:" << str << std::endl;
        std::string::const_iterator iter( str.begin() );
        std::string::const_iterator last( str.end() );
        std::vector< std::string > v;

        qi::rule<std::string::const_iterator,
            qi::locals< std::vector<std::string>  >,
            ascii::space_type ,std::vector<std::string>()> mrule =
                ( qi::as_string[ qi::lexeme[ +(qi::graph -"/"-"[") ] ][ ph::push_back( qi::_val,qi::_1 )] |
                    (
                        qi::lit("[")
                            >> -(
                                    qi::eps[ ph::clear( qi::_a ) ]
                                    >> qi::as_string[ qi::lexeme[ +(qi::graph-"/"-"]") ] ][  ph::push_back( qi::_a ,qi::_1 ) ]
                                    % qi::lit("/")
                                )
                    )
                    >> qi::lit("]" )
                    >> qi::int_[ inserter( qi::_val,qi::_a,qi::_1 ) ]
                )
                % qi::lit("/");

        if( qi::phrase_parse( iter, last, mrule , ascii::space, v ) && iter==last )
            std::copy( v.begin(), v.end(), 
                       std::ostream_iterator<std::string>( std::cout,"\n" ));
        else
           std::cerr << "parsing failed:" << *iter << std::endl;
    }
    return 0;
}

您可以进一步简化mrule,以便自动合成属性而不是使用语义操作 - 即使您不会完全避免它们:

qi::rule<std::string::const_iterator,
    qi::locals< std::vector<std::string>  >,
    ascii::space_type ,std::vector<std::string>()> mrule;
    mrule %=
        (
            qi::as_string[ qi::lexeme[ +(qi::graph -"/"-"[") ] ] |                      
                qi::lit("[")
                    >> -(
                            qi::eps[ ph::clear( qi::_a ) ]
                            >> qi::as_string[ qi::lexeme[ +(qi::graph-"/"-"]") ] ][ ph::push_back( qi::_a ,qi::_1 ) ]
                            % qi::lit("/")
                        )
            >> qi::lit("]" )
            >> qi::omit[ qi::int_[ inserter( qi::_val,qi::_a,qi::_1-1 ) ] ]
        )
        % qi::lit("/");

由于sehe指向一些丑陋的结构,这里是一个小的简化:

qi::rule<std::string::const_iterator,
    qi::locals< std::vector<std::string>  >,
    ascii::space_type ,std::vector<std::string>()> mrule;
mrule %= (
            qi::as_string[ qi::lexeme[ +qi::alnum ] ] |
            qi::lit("[")
               >> -(
                   qi::eps[ ph::clear( qi::_a ) ] >>
                   qi::as_string[ qi::lexeme[ +qi::alnum ] ][ ph::push_back( qi::_a ,qi::_1 ) ]
                   % qi::lit("/")
                   )
                   >> qi::lit("]")
                   >> qi::omit[ qi::int_[ inserter( qi::_val,qi::_a,qi::_1-1 ) ] ]
            ) % qi::lit("/");