正则表达式中的递归模式

时间:2014-10-15 15:11:35

标签: python regex recursive-regex

这与Regular Expression to match outer brackets非常相关,但我特别想知道如何或是否可以这样做regex's recursive pattern我还没有找到使用此策略的python示例,所以认为这应该是一个有用的问题!

seen some claims that递归模式可用于匹配平衡括号,但没有使用python的regex包的示例(注意:重新支持递归模式,你需要使用正则表达式。)

一个claim的语法是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(在匹配对象上),请参阅here。)

所以我曾希望以下或某些变体可以起作用:

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打破了。

是否可以使用正则表达式的递归为外括号提取匹配对象?


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

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

2 个答案:

答案 0 :(得分:39)

模式是:

{((?>[^{}]+|(?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"角色无法回馈。只有整个团队才能被退回。

[编辑]:如果您使用"展开"可以更有效地编写模式。子模式描述括号之间的内容:

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

答案 1 :(得分:10)

我能够使用b(?:m|(?R))*e语法执行此操作:

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

Demo


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