这与正则表达式匹配外括号非常相关,但是,我特别想知道如何或是否可以这样做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)
引用进行递归的原因。