使用 RegEx 平衡匹配括号
Posted
技术标签:
【中文标题】使用 RegEx 平衡匹配括号【英文标题】:Using RegEx to balance match parenthesis 【发布时间】:2011-12-15 10:43:09 【问题描述】:我正在尝试创建一个 .NET RegEx 表达式,以正确平衡我的括号。我有以下正则表达式:
func([a-zA-Z_][a-zA-Z0-9_]*)\(.*\)
我要匹配的字符串是这样的:
"test -> funcPow((3),2) * (9+1)"
应该发生的是正则表达式应该匹配从funcPow
到第二个右括号的所有内容。它应该在第二个右括号之后停止。相反,它一直匹配到最后一个右括号。正则表达式返回这个:
"funcPow((3),2) * (9+1)"
它应该返回这个:
"funcPow((3),2)"
对此的任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:正则表达式绝对可以做到平衡括号匹配。这可能很棘手,并且需要一些更高级的正则表达式功能,但并不难。
例子:
var r = new Regex(@"
func([a-zA-Z_][a-zA-Z0-9_]*) # The func name
\( # First '('
(?:
[^()] # Match all non-braces
|
(?<open> \( ) # Match '(', and capture into 'open'
|
(?<-open> \) ) # Match ')', and delete the 'open' capture
)+
(?(open)(?!)) # Fails if 'open' stack isn't empty!
\) # Last ')'
", RegexOptions.IgnorePatternWhitespace);
平衡匹配组有几个功能,但在此示例中,我们仅使用捕获删除功能。 (?<-open> \) )
行将匹配 )
并删除之前的“打开”捕获。
最棘手的一行是(?(open)(?!))
,所以让我解释一下。 (?(open)
是一个条件表达式,仅在存在“打开”捕获时才匹配。 (?!)
是一个总是失败的否定表达式。因此,(?(open)(?!))
表示“如果有打开的捕获,则失败”。
Microsoft's documentation 也很有帮助。
【讨论】:
我更改了[^()]* # Match all non-braces
行,所以它匹配 () 里面什么都没有【参考方案2】:
使用平衡组,它是:
Regex rx = new Regex(@"func([a-zA-Z_][a-zA-Z0-9_]*)\(((?<BR>\()|(?<-BR>\))|[^()]*)+\)");
var match = rx.Match("funcPow((3),2) * (9+1)");
var str = match.Value; // funcPow((3),2)
(?<BR>\()|(?<-BR>\))
是一个Balancing Group(我用来命名的BR
是Brackets
)。这样更清楚(?<BR>
\()|(?<-BR>
\))
也许,让\(
和\)
更“明显”。
如果你真的很讨厌自己(以及世界/你的同事)使用这些东西,我建议使用 RegexOptions.IgnorePatternWhitespace
并在任何地方“洒”空白 :-)
【讨论】:
我认为你错过了最后一个关键部分,(?(BR)(?!))
@ScottRippey 没有。)
结束后还有其他表达式。 OP问题非常精确。他想要funcsomething()
,而不是解析整个表达式。所以我找到的第一个“不平衡”括号是我的子表达式的右括号。 funcPow((3),2) * (9+1) -> funcPow((3),2)
哦,我意识到(?(BR)(?!))
只是为了确保左大括号有一个右大括号。微软网站:“最后的子表达式 (?(Open)(?!)) 表示输入字符串中的嵌套结构是否正确平衡”
这是一个很好的讨论 :-) 我们想法一致。太像了现在我必须摧毁你。对不起。
只是为了记录,包括(?(BR)(?!))
和不包括它之间的区别在于,没有它,表达式将匹配直到并包括最后一个右括号如果没有 enough 右括号。有了它,整个表达式将不匹配。【参考方案3】:
正则表达式仅适用于 Regular Languages。这意味着正则表达式可以找到“a 和 b 的任意组合”之类的东西。(ab
或babbabaaa
等)但是他们找不到“n a's, one b's , n a's".(a^n b a^n
) 正则表达式不能保证第一组a匹配第二组a。
因此,它们无法匹配相同数量的左括号和右括号。编写一个一次遍历字符串一个字符的函数很容易。有两个计数器,一个用于打开paren,一个用于关闭。如果 opening_paren_count != closing_parent_count
返回 false,则在遍历字符串时递增指针。
【讨论】:
可能是这样,但正则表达式几乎可以用于任何类型的文本,只要您了解它们的局限性。递归/平衡模式很难看,而且 (IMO) 很少值得努力,但它们受到许多正则表达式风格的支持。【参考方案4】:func[a-zA-Z0-9_]*\((([^()])|(\([^()]*\)))*\)
您可以使用它,但如果您使用 .NET,可能会有更好的选择。
这部分你已经知道了:
func[a-zA-Z0-9_]*\( --weird part-- \)
--weird 部分-- 部分只是意味着; (
允许任何字符 .
或 |
任何部分 (.*)
存在任意多次 )*
。唯一的问题是,你不能匹配任何字符.
,你必须使用[^()]
来排除括号。
(([^()])|(\([^()]*\)))*
【讨论】:
您应该指定这只适用于一层嵌套。 @ScottRippey:如果该函数中有一个函数,它仍然有效。该|条件处理。你能举一个这个正则表达式会提供错误匹配的例子吗? 它工作正常,并且完全按照 OP 的要求执行,所以这是一个很好的答案。但是,它被硬编码为仅匹配一级嵌套,因此无法匹配:func(a(b(c)d)e)
。目前尚不清楚 OP 是否需要这个。以上是关于使用 RegEx 平衡匹配括号的主要内容,如果未能解决你的问题,请参考以下文章