提升精神语法规则,只提取字母数字标记

时间:2013-05-28 21:46:23

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

对于字母数字,我有一个lexeme。

attributes = lexeme [+(boost :: spirit :: qi :: alpha | boost :: spirit :: qi :: digit)];

我希望有一个语法规则可以跳过任何其他不符合此规则的字符,只需将这些字符放在矢量中即可。

例如:输入:STR1 + STR2%STR3()STR4 = STR5 + STR6

           output: (STR1, STR2, STR3, STR4, STR6)

我尝试了下面的语法,但是在解析字符串中的第一个单词后它会跳过所有内容。如何按照我的描述将其更改为解析?

typedef std::vector<std::wstring> Attributes;
template <typename It, typename Skipper=boost::spirit::qi::space_type>
struct AttributeParser : boost::spirit::qi::grammar<It, Attributes(),  Skipper>
{
    AttributeParser() : AttributeParser::base_type(expression)
    {
        expression = 

            *( attributes [phx::push_back(qi::_val, qi::_1)])
            >> qi::omit[*qi:char_]
            ;

        attributes = qi::lexeme[+(boost::spirit::qi::alpha|qi::boost::spirit::qi::digit)];

        BOOST_SPIRIT_DEBUG_NODE(expression);
        BOOST_SPIRIT_DEBUG_NODE(attributes);
    }


private:
    boost::spirit::qi::rule<It, std::wstring() , Skipper> attributes;
    boost::spirit::qi::rule<It, Attributes() , Skipper> expression;

};

3 个答案:

答案 0 :(得分:2)

我真的会写下你描述的内容:

    std::vector<std::wstring> parsed;
    bool ok = qi::phrase_parse(
            begin(input), end(input),
            *qi::lexeme [ +qi::alnum ],
            ~qi::alnum,
            parsed);

即:

  • 解析(部分)输入
  • 解析alpha-numerics的词汇
  • 跳过任何非字母数字
  • 将结果放入parsed向量

这是完整的程序

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

namespace qi = boost::spirit::qi;

int main()
{
    std::wstring input = L"STR1 + STR2 % STR3 () STR4 = STR5+ STR6";

    std::vector<std::wstring> parsed;
    bool ok = qi::phrase_parse(begin(input), end(input),
            *qi::lexeme [ +qi::alnum ],
            ~qi::alnum,
            parsed);

    for(auto& v : parsed)
        std::wcout << v << std::endl;
}

打印

STR1
STR2
STR3
STR4
STR5
STR6

答案 1 :(得分:1)

在这里,您要解析第一个字符串并将其推入向量:

*( attributes [phx::push_back(qi::_val, qi::_1)])

接下来,您将省略任何可以转换为 char 的内容:

>> qi::omit[*qi:char_]

所以你基本上告诉你的解析器跳过字符串的其余部分,无论字符是否为字母数字。如果您希望它工作,您需要更改

qi::omit[*qi::char_] 

类似

qi::omit[*(qi::char_ - qi::alnum)]. 

并且应该省略除字母数字之外的任何字符,这应该是您要保留的下一个字符串的开头。我现在无法尝试代码,但你明白了。

答案 2 :(得分:0)

除非你致力于使用Spirit(例如,这只是使用Spirit的一小部分,否则),我会用自定义ctype facet来做这件事。

struct alpha_num: std::ctype<char> {
    alpha_num(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table() {
        // As far as we care, everything is white-space:
        static std::vector<std::ctype_base::mask> 
            rc(std::ctype<char>::table_size,std::ctype_base::space);

        // except digits and letters:
        std::fill(&rc['0'], &rc['9'], std::ctype_base::digit);
        std::fill(&rc['a'], &rc['z'], std::ctype_base::alpha);
        std::fill(&rc['A'], &rc['Z'], std::ctype_base::alpha);
        return &rc[0];
    }
};

从那里开始,打开文件,使用该ctype facet创建一个区域设置,并读取令牌,这是一个相当简单的问题。这是您输入的快速测试:

int main() { 

    std::istringstream infile("STR1 + STR2 % STR3 () STR4 = STR5+ STR6");
    infile.imbue(std::locale(std::locale(), new alpha_num));

    // Initialize vector from file:
    std::vector<std::string> tokens((std::istream_iterator<std::string>(infile)),
                                     std::istream_iterator<std::string>());

    // show the tokens:
    for (auto const & s : tokens)
        std::cout << s << "\n";
    return 0;
}

结果:

STR1
STR2
STR3
STR4
STR5
STR6