正则表达式中的递归模式



这与正则表达式匹配外括号非常相关,但是,我特别想知道如何或是否可以这样做regex的递归模式我还没有找到使用此策略的python示例,所以我认为这应该是一个有用的问题

我看到一些关于递归模式可以用来匹配平衡括号的说法,但没有使用python的regex包的例子(注意:re不支持递归模式,您需要使用regex)。

一种说法是语法是b(?:m|(?R))*e,其中:

b是构造的开始,m是构造中间可能发生的,e是构造结束时可能发生的


我想提取以下中外部大括号的匹配项:

"{1, {2, 3}} {4, 5}"
["1, {2, 3}", "4, 5"]  # desired

请注意,对于内部大括号,这很容易做到:

re.findall(r"{([^{}]*)}", "{1, {2, 3}} {4, 5}")
['2, 3', '4, 5']

(在我的示例中,我使用finditer(超过匹配对象),请参阅此处。)

因此,我曾希望以下或一些变体能起作用:

regex.findall(r"{(:[^{}]*|?R)}", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}]*|?R)})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*|(?R))*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*)|(?R)*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}])|(?R)})", "{1, {2, 3}} {4, 5}")

但我被[]或error: too much backtracking搞砸了。

是否可以使用regex的递归提取外括号的匹配对象


显然,我冒着被击落的风险

  • 不要用regex解析html
  • 使用pyparse执行此操作
  • 写一个合适的lexer&解析器,例如使用ply

我想强调的是,这是关于如何使用递归模式(如果我的理解是正确的,这将使我们脱离常规语言解析,所以实际上可能是可能的!)。如果可以做到,这应该是一个更清洁的解决方案。

模式为:

{((?>[^{}]+|(?R))*)}

你可以在你的例子中看到这一点:

regex.findall("{((?>[^{}]+|(?R))*)}", "{1, {2, 3}} {4, 5}")
# ['1, {2, 3}', '4, 5']

说明:

m部分需要排除括号。如果您希望同时允许[^{}]的量词,并在没有灾难回溯问题的情况下重复该组,则需要使用原子组。更清楚的是,如果最后一个结束大括号丢失,则正则表达式引擎将逐原子组回溯,而不是逐字符回溯。为了说明这一点,您可以使量词所有格如下:{((?>[^{}]+|(?R))*+)}(或{((?:[^{}]+|(?R))*+)},因为原子组不再有用)。

原子群(?>....)和所有格量词?+*+++是同一特征的两侧。此功能禁止正则表达式引擎在成为"原子"(不能分割成更小的部分)的字符组内回溯

基本示例是以下两种字符串aaaaaaaaaab总是失败的模式:

(?>a+)ab
a++ab

即:

regex.match("a++ab", "aaaaaaaaaab")
regex.match("(?>a+)ab", "aaaaaaaaaab")

当您使用(?:a+)a+时,正则表达式引擎(默认情况下)会记录(在预览中)所有字符的所有回溯位置。但当你使用原子群或所有格量词时,这些回溯位置就不再被记录(除了群的开头)。因此,当回溯机制发生时,最后一个"a"字符无法返回。只有整个团队才能得到回报。

[EDIT]:如果你使用一个"展开"的子模式来描述括号之间的内容,那么这个模式可以用一种更有效的方式编写:

{([^{}]*+(?:(?R)[^{}]*)*+)}

我能够做到这一点,b(?:m|(?R))*e语法没有问题:

{((?:[^{}]|(?R))*)}

演示


我认为你尝试的关键是,重复不是在m上,而是在整个(?:m|(?R))组上。这就是允许使用(?R)引用进行递归的原因。

最新更新