c++ - 增强精神解析器: getting around the greedy kleene *

标签 c++ parsing boost-spirit-qi

我有一个语法,应该匹配单个字符后面的字符序列,该单个字符是第一个字符的子集。 例如,

boost::spirit::qi::rule<Iterator, std::string()> grammar = *char_('a', 'z') >> char_('b', 'z').

由于 kleene * 是贪婪运算符,它会吞噬所有内容,为第二个解析器留下任何内容,因此它无法匹配像“abcd”这样的字符串

有什么办法可以解决这个问题吗?

最佳答案

是的,尽管您的样本缺乏我们了解的背景。

我们需要知道什么构成完整匹配,因为现在“b”将是有效匹配,“bb”或“bbb”将是有效匹配。那么当输入是“bbb”时,会匹配什么? (b、bb 或 bbb?)。

当你(可能)回答“显然,bbb”时,那么“bbbb”会发生什么?您什么时候停止从子集中接受字符?如果你希望 kleene star 不贪婪,你希望它仍然贪婪吗?

上面的对话框很烦人,但目的是让您思考您需要什么。你不需要需要一个非贪婪的小明星。您可能需要对最后一个字符进行验证约束。最有可能的是,如果输入有“bbba”,您不想想要简单地匹配“bbb”,而留下“a”。相反,您可能想停止解析,因为“bbba”不是有效的标记。

假设...

我会写

grammar = +char_("a-z") >> eps(px::back(_val) != 'a');

这意味着只要匹配,我们就接受至少 1 个字符,断言最后一个字符不是 a

Live On Coliru

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

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

template <typename It>
struct P : qi::grammar<It, std::string()>
{
    P() : P::base_type(start) {
        using namespace qi;
        start = +char_("a-z") >> eps(px::back(_val) != 'a');
    }
  private:
    qi::rule<It, std::string()> start;
};

#include <iomanip>

int main() {
    using It = std::string::const_iterator;
    P<It> const p;

    for (std::string const input : { "", "b", "bb", "bbb", "aaab", "a", "bbba" }) {
        std::cout << std::quoted(input) << ": ";
        std::string out;
        It f = input.begin(), l = input.end();
        if (parse(f, l, p, out)) {
            std::cout << std::quoted(out);
        } else {
            std::cout << "(failed) ";
        }

        if (f != l)
            std::cout << " Remaining: " << std::quoted(std::string(f,l));
        std::cout << "\n";
    }
}

打印

"": (failed) 
"b": "b"
"bb": "bb"
"bbb": "bbb"
"aaab": "aaab"
"a": (failed)  Remaining: "a"
"bbba": (failed)  Remaining: "bbba"

奖金

一种更通用但效率较低的方法是将前导字符与前瞻断言相匹配,即它不是同类的最后一个字符:

start = *(char_("a-z") >> &char_("a-z")) >> char_("b-z");

这里的一个好处是不需要使用 Phoenix:

Live On Coliru

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

namespace qi = boost::spirit::qi;

template <typename It>
struct P : qi::grammar<It, std::string()>
{
    P() : P::base_type(start) {
        using namespace qi;
        start = *(char_("a-z") >> &char_("a-z")) >> char_("b-z");
    }
  private:
    qi::rule<It, std::string()> start;
};

#include <iomanip>

int main() {
    using It = std::string::const_iterator;
    P<It> const p;

    for (std::string const input : { "", "b", "bb", "bbb", "aaab", "a", "bbba" }) {
        std::cout << std::quoted(input) << ": ";
        std::string out;
        It f = input.begin(), l = input.end();
        if (parse(f, l, p, out)) {
            std::cout << std::quoted(out);
        } else {
            std::cout << "(failed) ";
        }

        if (f != l)
            std::cout << " Remaining: " << std::quoted(std::string(f,l));
        std::cout << "\n";
    }
}

关于c++ - 增强精神解析器: getting around the greedy kleene *,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48478423/

相关文章:

c++ - 初学者在 2D 网格上与 Lee 算法作斗争

java - JSON VS 简单的字符串操作来解析 Android 中的 HttpRequest

c++ - 从 Boost.Spirit.Qi 制作shared_ptr

c++ - Boost 精神解析器未编译

c++ - 确保编译时模板类型的一致性

C++:从 std::map 继承

json - 如何解析 JSON 中的特殊字符?

c++ - 使用 Boost Spirit 解析语法的未处理异常

c++ - 如何填充数据结构列表?

java - 用于 Java 的 GPX 解析器?