StackOverflow鼓励自己回答问题,所以我决定创建这篇文章来分享我最近发现的东西。
问题:在正则表达式中匹配任意嵌套的括号组,例如Java' s java.util.regex ,既不支持递归也不支持平衡组。即,匹配3个外部组:
(F(i(r(s)t)))((S)(e)((c)(o))(n)d)((((((((Third)))))) )
这个练习纯粹是学术性的,因为我们都知道正则表达式不应该被用来匹配这些东西,就像Q-tips不应该被用来清理耳朵一样。
答案 0 :(得分:30)
(?=\()(?:(?=.*?\((?!.*?\1)(.*\)(?!.*\2).*))(?=.*?\)(?!.*?\2)(.*)).)+?.*?(?=\1)[^(]*(?=\2$)
Et voila ;它就是。在那里,从头到尾匹配一组完整的嵌套括号。每次匹配必须捕获并保存两个子串;这对你没用。只关注主要比赛的结果。
不,深度没有限制。不,那里没有隐藏的递归结构。只是简单的'lookarounds,带有前瞻性的参考。如果你的味道不支持前向引用(我在看你,JavaScript),那我很抱歉。我真的是。我希望我能帮助你,但我不是一个奇迹般的奇迹工作者。
(?=\()(?=((?:(?=.*?\((?!.*?\2)(.*\)(?!.*\3).*))(?=.*?\)(?!.*?\3)(.*)).)+?.*?(?=\2)[^(]*(?=\3$)))
与上一个表达式完全相同,除了我将它的大部分包装在一个预测中以避免消耗字符,添加一个捕获组,并调整后向引用索引以便它们与新朋友一起玩得很好。现在表达式匹配在下一个括号组之前的位置,并且感兴趣的子字符串保存为\ 1.
我很高兴你问。一般方法非常简单:一次迭代一个字符,同时匹配下一次出现的'('和')',在每种情况下捕获字符串的其余部分,以便建立从中继续搜索的位置。下一次迭代。让我一块一块地分解:
所以,你有它。使用前向引用和标准(扩展)正则表达式功能匹配平衡嵌套结构的方法 - 无递归或平衡组。它效率不高,而且肯定不漂亮,但它是可能的。而且以前从未做过。对我来说,这非常令人兴奋。
我知道很多人使用正则表达式来完成并帮助其他用户完成更简单和更实际的任务,但是如果有人与我分享我对用正则表达式推动可能性极限的兴奋那么我会喜欢听到你的消息。如果有兴趣,我还有其他类似材料可以发布。
答案 1 :(得分:7)
首先,您的输入不正确,因为有一个额外的括号(如下所示)
(F(i(r(s)t))) ((S)(e)((c)(o))n)d) (((((((Third)))))))
^
对包括或排除附加括号的进行适当修改,最终可能会出现以下字符串之一:
删除了额外的括号
(F(i(r(s)t))) ((S)(e)((c)(o))n)d (((((((Third)))))))
^
添加了附加括号以匹配额外的右括号
((F(i(r(s)t))) ((S)(e)((c)(o))n)d) (((((((Third)))))))
^
其次,这真的只能在包含递归功能的正则表达式中实现,因为任何其他方法都不能正确匹配开/关括号(如OP的解决方案中所见,它匹配来自错误输入的额外括号如上所述)。
这意味着对于当前支持递归(Java,Python,JavaScript等)的正则表达式,正则表达式中的递归(或尝试模拟递归)不< / strong>可能。
考虑到原始输入实际上是无效的,我们将使用以下输入进行测试。
(F(i(r(s)t))) ((S)(e)((c)(o))n)d) (((((((Third)))))))
(F(i(r(s)t))) ((S)(e)((c)(o))n)d (((((((Third)))))))
((F(i(r(s)t))) ((S)(e)((c)(o))n)d) (((((((Third)))))))
针对这些输入进行测试应产生以下结果:
有多种匹配嵌套组的方法。下面提供的解决方案都依赖于包含递归功能的正则表达式风格(例如PCRE)。
(?(DEFINE)
(?<value>[^()\r\n]+)
(?<groupVal>(?&group)|(?&value))
(?<group>(?&value)*\((?&groupVal)\)(?&groupVal)*)
)
^(?&group)$
注意:此正则表达式使用标记gmx
^(?<group>
(?<value>[^()\r\n]+)*
\((?<groupVal>(?&group)|(?&value))\)
(?&groupVal)*
)$
注意:此正则表达式使用标记gmx
^(?<group>(?<value>[^()\r\n]+)*\((?<groupVal>(?&group)|(?&value))\)(?&groupVal)*)$
^(([^()\r\n]+)*\(((?1)|(?2))\)(?3)*)$
注意:这是我能想到的最简短的方法。
我将解释最后一个正则表达式,因为它是上面所有其他正则表达式的简化和最小的例子。
^
在行首处断言位置(([^()\r\n]+)*\(((?1)|(?2))\)(?3)*)
将以下内容捕获到捕获组 1 中
([^()\r\n]+)*
将以下内容捕获到捕获组 2 中
[^()\r\n]+
匹配集()\r\n
中不存在的任何字符\(
字面上匹配左/左括号字符(
((?1)|(?2))
将以下任意一项捕获到捕获组 3 中
(?1)
递归第一个子模式(1)(?2)
递归第二个子模式(2)\)
匹配右/右括号字符)
字面意思(?3)*
递归第三个子模式(3)$
断言行尾的位置