我想解析一个可以包含“-”的字符串,但不能以它开头或结尾。

我希望这个解析器能够工作:

auto const parser = alnum >> -(*(alnum | char_('-')) >> alnum);

但是在我的测试输入中,“something”仅解析“so”,不占用其余部分。

问题在于中间的*(alnum | char_('-'))一直吃到最后(包括最后一个字符,因此整个可选括号都失败了)。

herehere的解释方式和原因

我想知道的是,我如何解决它并使它成为解析器?

现场观看:http://coliru.stacked-crooked.com/a/833cc2aac7ba5e27

最佳答案

我个人会“积极地”写它:

auto const rule = raw [ lexeme [
    alnum >> *('-' >> alnum | alnum) >> !(alnum|'-')
] ];

这使用
  • lexeme处理空格的重要性
  • raw避免必须主动匹配每个字符作为输出的一部分(您只想要所有字符)。
  • '-' >> alnum积极要求任何破折号后面应加一个数字。请注意,这也禁止输入中的"--"。请参阅
  • 下的 VARIANT

    Live On Coliru
    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <string>
    #include <algorithm>
    
    namespace x3 = boost::spirit::x3;
    
    namespace parser {
        using namespace boost::spirit::x3;
    
        auto const rule = raw [ lexeme [
            alnum >> *('-' >> alnum | alnum) >> !(alnum|'-')
        ] ];
    }
    
    int main() {
        struct test { std::string input; bool expected; };
    
        for (auto const t : {
                test { "some-where", true },
                test { " some-where", true },
                test { "some-where ", true },
                test { "s", true },
                test { " s", true },
                test { "s ", true },
                test { "-", false },
                test { " -", false },
                test { "- ", false },
    
                test { "some-", false },
                test { " some-", false },
                test { "some- ", false },
    
                test { "some--where", false },
                test { " some--where", false },
                test { "some--where ", false },
            })
        {
            std::string output;
            bool ok = x3::phrase_parse(t.input.begin(), t.input.end(), parser::rule, x3::space, output);
            if (ok != t.expected)
                std::cout << "FAILURE: '" << t.input << "'\t" << std::boolalpha << ok << "\t'" << output << "'\n";
        }
    }
    

    变体

    为了也允许some--thing和类似的输入,我将'-'更改为+lit('-'):
    alnum >> *(+lit('-') >> alnum | alnum) >> !(alnum|'-')
    

    Live On Coliru
    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <string>
    #include <algorithm>
    
    namespace x3 = boost::spirit::x3;
    
    namespace parser {
        using namespace boost::spirit::x3;
    
        auto const rule = raw [ lexeme [
            alnum >> *(+lit('-') >> alnum | alnum) >> !(alnum|'-')
        ] ];
    }
    
    int main() {
        struct test { std::string input; bool expected; };
    
        for (auto const t : {
                test { "some-where", true },
                test { " some-where", true },
                test { "some-where ", true },
                test { "s", true },
                test { " s", true },
                test { "s ", true },
                test { "-", false },
                test { " -", false },
                test { "- ", false },
    
                test { "some-", false },
                test { " some-", false },
                test { "some- ", false },
    
                test { "some--where", true },
                test { " some--where", true },
                test { "some--where ", true },
            })
        {
            std::string output;
            bool ok = x3::phrase_parse(t.input.begin(), t.input.end(), parser::rule, x3::space, output);
            if (ok != t.expected)
                std::cout << "FAILURE: '" << t.input << "'\t" << std::boolalpha << ok << "\t'" << output << "'\n";
        }
    }
    

    09-06 20:04