C#中匹配句号前平衡括号内字符串的正则匹配问题
搞定C#中平衡括号+句号前的正则匹配问题
嘿,我明白你现在的困扰——想用带平衡组的正则匹配句号前、完全闭合的括号内的字符串,但遇到多余左括号时就失效了。咱们来一步步捋清楚问题,然后给出靠谱的解决方案。
先说说你现有正则的问题
你原来的正则@"(?<Par>[(]).+(?<-Par>[)])(?(Par)(?!))\."和修改后的版本,主要有两个坑:
.+太贪婪:它会直接匹配到最后一个右括号,完全不管中间的嵌套括号,导致平衡组的计数完全乱掉,根本没法正确统计括号的开闭情况。- 条件断言的位置不对:就算加了
(?(Par)(?!)),但因为前面的贪婪匹配已经走了错误的路径,这个断言没法提前阻止错误的匹配尝试。
修正后的正则方案
我给你调整了正则的结构,改用原子组配合平衡组,同时严格确保括号完全平衡,而且只匹配句号前的有效内容:
@"\((?>[^()]+|(?<Par>\()|(?<-Par>\)))+(?(Par)(?!))\."
给你拆解开解释下:
\(:先锁定开头的左括号,确保我们从合法的括号起始处开始匹配(?>[^()]+|(?<Par>\()|(?<-Par>\)))+:这个原子组是核心,避免回溯干扰平衡计数:[^()]+:匹配任何非括号的普通字符(?<Par>\():碰到左括号,就往Par栈里加一个标记(?<-Par>\)):碰到右括号,就从Par栈里删掉一个标记+:重复这个逻辑,直到没法匹配为止
(?(Par)(?!)):关键的条件断言——如果Par栈里还有剩余标记(也就是还有未闭合的左括号),直接让匹配失败\.:最后匹配结尾的句号
测试验证一下
把这个正则替换到你的测试代码里,之前失败的用例就能正常工作了:
string[] tests = { "a.c", "", "a).c", "", "(a.c", "", "a(a).c", "(a).", "a(a b).c", "(a b).", "a((a b)).c", "((a b)).", "a(((a b))).c", "(((a b))).", "a((a) (b)).c", "((a) (b)).", "a((a)(b)).c", "((a)(b)).", "a((ab)).c", "((ab)).", "a)((ab)).(c", "((ab)).", "a(((a b)).c", "", "a(((a b))).)c", "(((a b)))." }; Regex re = new Regex(@"\((?>[^()]+|(?<Par>\()|(?<-Par>\)))+(?(Par)(?!))\."); for (int i = 0; i < tests.Length; i += 2) { var result = re.Match(tests[i]).Groups[0].Value; if (result != tests[i + 1]) throw new Exception ("Expecting: " + tests[i + 1] + ", got " + result); }
比如之前失败的"a(((a b)).c",因为里面的左括号没完全闭合,条件断言会直接让匹配失败,返回空字符串,完全符合你的预期。
额外提个小细节
用原子组(?>...)是为了避免不必要的回溯,既能提升正则的运行效率,还能保证平衡组的计数不会因为回溯被打乱,特别适合处理嵌套结构的匹配。
内容的提问来源于stack exchange,提问作者rednoyz
相关产品推荐
相关产品推荐

