详细讲解平衡组(2)

对于一个嵌套结构而言,开始和结束标记都是确定的,对于本例开始为“(”,结束为“)”,那么接下来就是考察中间的结构,中间的字符可以划分为三类,一类是“(”,一类是“)”,其余的就是除这两个字符以外的任意字符。

那么平衡组的匹配原理就是这样的

1、先找到第一个“(”,作为匹配的开始。即上面的第1行,匹配了:a+(b*(c+d))/e+f-(g/(h-i))*j (红色显示部分)

2、在第1步以后,每匹配到一个“(”,就入栈一个Open捕获组,计数加1

3、在第1步以后,每匹配到一个“)”,就出栈最近入栈的Open捕获组,计数减1

也就是讲,上面的第一行正则“\(”匹配了:a+(b*(c+d))/e+f-(g/(h-i))*j (红色显示部分)
然后,匹配到c前面的“(”,此时,计数加1;继续匹配,匹配到d后面的“)”,计算减1;——注意喽:此时堆栈中的计数是0,正则还是会向前继续匹配的,但是,如果匹配到“)”的话,比如,这个例子中d))(红色显示的括号)——引擎此时将控制权交给(?(Open)(?!)),判断堆栈中是否为0,如果为0,则执行匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
这个正则表达式“\)”可匹配接下来的),即b))(红色显示的括号)

4、后面的 (?(Open)(?!))用来保证堆栈中Open捕获组计数是否为0,也就是“(”和“)”是配对出现的

5、最后的“)”,作为匹配的结束

匹配过程

首先匹配第一个“(”,然后一直匹配,直到出现以下两种情况之一时,把控制权交给(?(Open)(?!)):
a)堆栈中Open计数已为0,此时再遇到“)”
b)匹配到字符串结束符
这时控制权交给(?(Open)(?!)),判断Open是否有匹配,由于此时计数为0,没有匹配,那么就匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
如果上面遇到的是情况a),那么此时“\)”可以匹配接下来的“)”,匹配成功;
如果上面遇到的是情况b),那么此时会进行回溯,直到“\)”匹配成功为止,否则报告整个表达式匹配失败。
由于.NET中的狭义平衡组“(?<Close-Open>Expression)”结构,可以动态的对堆栈中捕获组进行计数,匹配到一个开始标记,入栈,计数加1,匹配到一个结束标记,出栈,计数减1,最后再判断堆栈中是否还有Open,有则说明开始和结束标记不配对出现,不匹配,进行回溯或报告匹配失败;如果没有,则说明开始和结束标记配对出现,继续进行后面子表达式的匹配。
需要对“(?!)”进行一下说明,它属于顺序否定环视,完整的语法是“(?!Expression)”。由于这里的“Expression”不存在,表示这里不是一个位置,所以试图尝试匹配总是失败的,作用就是在Open不配对出现时,报告匹配失败。

下面在看个例子:

<table> <tr> <td> </td> <td> <table> <tr> <td>snhame</td> <td>f</td> </tr> </table> </td> <td></td> </tr> </table>

以上为部分的HTML代码.现在我们的问题是要提取出其<td>的<td>标签并将其删除掉,以往我们惯用的方法都是直接去取,像<td\s*id="td2">[\s\S]+?\</td>,不过问题出来了,我们提取到的不是我们想要的内容,而是

<td> <table> <tr> <td>snhame</td>

原因也很简单,它和离他最近的</td>标签匹配上了,不过它不知道这个标签不是它的-_-,是不是就是?符号的原因呢,我们去掉让他无限制贪婪,可这下问题更大了,什么乱七八糟的东东它都匹配到了

<td> <table> <tr> <td>snhame</td> <td>f</td> </tr> </td> <td></td>

这个结果也不是我们想要的。那么我就用“平衡组”来解决吧。

<td\s*id="td2"[^>]*>((?<mm><td[^>]*>)+|(?<-mm></td>)|[\s\S])*?(?(mm)(?!))</td>

匹配的结果是

<td> <table> <tr> <td>snhame</td> <td>f</td> </tr> </table> </td> <td></td>

这正是我们想要的
注意,我开始写成这样的方式

<td\s*id="td2"[^>]*>((?<mm><td[^>]*>)+|(?<-mm></td>)|[\s\S])*(?(mm)(?!))</td>

匹配的结果是

<td> <table> <tr> <td>snhame</td> <td>f</td> </tr> </table> </td> <td></td>

一个问题
以下代码只是做为一个问题探讨
文本内容:e+f(-(g/(h-i))*j

正则表达式:

\( ( (?<mm>\() | (?<-mm>\)) | . )*? (?(mm)(?!)) \)

匹配的结果是:(-(g/(h-i))

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wjsgjx.html