正则表达式中的递归模式

And*_*den 47 python regex recursive-regex

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

我已经看到 了一些 索赔 递归的模式可以用来匹配平衡括号,但使用Python的没有例子正则表达式包(注:重支持递归模式,你需要使用正则表达式).

一种说法是语法在b(?:m|(?R))*e哪里:

b是什么开始构造,m是什么可以发生在构造的中间,并且e是在构造的末尾可以发生的


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

"{1, {2, 3}} {4, 5}"
["1, {2, 3}", "4, 5"]  # desired
Run Code Online (Sandbox Code Playgroud)

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

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

(在我的例子中,我使用的是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}")
Run Code Online (Sandbox Code Playgroud)

但我被[]或者[...]打败了error: too much backtracking.

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


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

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

Cas*_*yte 45

模式是:

{((?>[^{}]+|(?R))*)}
Run Code Online (Sandbox Code Playgroud)

您可以看到这适用于您的示例:

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

说明:

m部分需要排除括号.如果您希望同时允许量词化[^{}]并重复该组而没有灾难性的回溯问题,则需要使用原子组.更清楚的是,如果缺少最后一个结束花括号,这个正则表达式引擎将按原子组而不是逐个字符地回溯原子组.为了在这一点上开车回家,你可以使量化器具有这样的占有欲:( {((?>[^{}]+|(?R))*+)}或者{((?:[^{}]+|(?R))*+)}因为原子组不再有用).

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

基本示例是以下两种始终因字符串而失败的模式aaaaaaaaaab:

(?>a+)ab
a++ab
Run Code Online (Sandbox Code Playgroud)

那是:

regex.match("a++ab", "aaaaaaaaaab")
regex.match("(?>a+)ab", "aaaaaaaaaab")
Run Code Online (Sandbox Code Playgroud)

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

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

{([^{}]*+(?:(?R)[^{}]*)*+)}
Run Code Online (Sandbox Code Playgroud)

  • 你在开玩笑么!所以一个+不是一个*,哦,我的天啊,回想起来是如此明显!太棒了. (3认同)
  • @AndyHayden:不,你不能,因为正则表达式模块没有像回溯控制动词(来自Perl和PHP)这样的功能允许这样的东西:`$ res = preg_split('〜({(?> [^ {} ] + |(?1))*})(*SKIP)(*FAIL)|\s +〜',$ str);`.你所能做的就是使用findall/iter这种模式:`r'({(?> [^ {}] + |(?1))*})| [^\s {] +'`或相似的东西. (3认同)
  • @AndyHayden:`?>` 是一个[原子组](http://www.regular-expressions.info/atomic.html),他解释说,而`?:` 是一个[非捕获组](http://www.regular-expressions.info/atomic.html) ://stackoverflow.com/questions/3512471/non-capturing-group)。不确定我是否看过`??`。 (2认同)

Sam*_*Sam 10

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

{((?:[^{}]|(?R))*)}
Run Code Online (Sandbox Code Playgroud)

演示


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

  • @ hjpotter92这只在`regex`包中提供,而不是在std lib的`re`模块中. (5认同)
  • 它在regex101上的Python实现失败了 (2认同)
  • 标准 re 模块中有解决方案吗? (2认同)