正则表达式(五)平衡组

发表于2019-11-06,长度1356, 69个单词, 3分钟读完
Flag Counter

这里讲一下平衡组。平衡组是比零宽断言受到支持更少的一个特性,比如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:链接

Written on November 6, 2019
分类: dev, 标签: re
如果你喜欢,请赞赏! davelet