正则表达式(五)平衡组
这里讲一下平衡组。平衡组是比零宽断言受到支持更少的一个特性,比如Java8就不支持。前面的具名组、零宽断言,如果你找不到编辑器支持他们,可以使用浏览器的控制台,因为JavaScript是支持正则比较广泛的语言,但是平衡组复杂到连JavaScript也还没支持,似乎C#在支持,还有python。
perl还支不支持我不清楚了
平衡组,故名思义,平衡即对称,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配。平衡组有狭义与广义两种定义,狭义平衡组指(?exp) 语法,而广义平衡组并不是固定的语法规则,而是几种语法规则的综合运用,平时所说的平衡组通常指的是广义平衡组。
比如有时需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那匹配结果里两者的个数也不会相等。 有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?平衡组就应运而生了!
先看一下狭义平衡组的语法:
- (?'group') 把捕获的内容命名为group,并压入栈
- (?'-group') 从栈上弹出最后入栈的名为group的捕获内容,如果栈本来为空,则本分组的匹配失败
- (?(group)yes|no) 如果栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
平衡组思路简单但语法复杂,这成为它难以推广使用的最大障碍。我有时间学习平衡组,早用这段时间写出一段代码实现类似能力了。平衡组有问题调试不方便,潜在问题也难以通过测试发现(正则通病)。这估计也是正则的窘境,正则难以进化正在于此吧。
来看一个网上的例子:找出a+(b(c+d))/e+f-(g/(h-i))j中的最大括号范围
这个需求的正则平衡组语法是(首先要明确不用平衡组而用其他正则语法是解决不了的)
\(((?<Open>\()|(?<−Open>\))|[^()]+)*(?(Open)(?!))\)
解析过程如下:
\( #普通字符“(”
( #分组构造,用来限定量词“*”修饰范围
(?<Open>\() #命名捕获组,遇到开括弧“Open”计数加1
| #分支结构
(?<-Open>\)) #狭义平衡组,遇到闭括弧“Open”计数减1
| #分支结构
[^()]+ #非括弧的其它任意字符
)* #以上子串出现0次或任意多次
(?(Open)(?!)) #判断是否还有“Open”,有则说明不配对,什么都不匹配
\) #普通闭括弧
大致意思就是分组中是:如果遇到左括号就入栈,遇到右括号就出栈,遇到其他字符啥也不干。这个分组执行任意次以后判断,栈上还有东西吗?有就是(?!)。(?!)是啥意思,就是负先行断言,表示总命不中。
完整PPT:链接
