2       平衡组匹配原理

2.1     预备知识

2.1.1  命名捕获组

(?’name’Expression)

string test = "abcdefghijkl";

Regex reg = new Regex(@"(?<chars>[a-z]{2})+");

Match m = reg.Match(test);

if (m.Success)

{

richTextBox2.Text += "匹配结果：" + m.Value + "\n";

richTextBox2.Text += "Group" + m.Groups["chars"].Value + "\n";

}

/*--------输出--------

Groupkl

*/

m.Groups["chars"].Value的输出上看，似乎确实是只保留了一个匹配内容，但却忽略了一个事实，Group实际上是Capture的一个集合

string test = "abcdefghijkl";

Regex reg = new Regex(@"(?<chars>[a-z]{2})+");

Match m = reg.Match(test);

if (m.Success)

{

richTextBox2.Text += "匹配结果：" + m.Value + "\n";

richTextBox2.Text += "Group" + m.Groups["chars"].Value + "\n--------------\n";

foreach (Capture c in m.Groups["chars"].Captures)

{

richTextBox2.Text += "Capture" + c + "\n";

}

}

/*--------输出--------

Groupkl

--------------

Captureab

Capturecd

Captureef

Capturegh

Captureij

Capturekl

*/

string test = "abcdefghijkl";

Regex reg = new Regex(@"(?<chars>[a-z]{2})");

Match m = reg.Match(test);

if (m.Success)

{

richTextBox2.Text += "匹配结果：" + m.Value + "\n";

richTextBox2.Text += "Group" + m.Groups["chars"].Value + "\n--------------\n";

foreach (Capture c in m.Groups["chars"].Captures)

{

richTextBox2.Text += "Capture" + c + "\n";

}

}

/*--------输出--------

Groupab

--------------

Captureab

*/

2.1.3  条件判断结构

(?(name)yes|no)

(?=Expression) ? yes : no

(?=Expression)”验证当前位置右侧是否能够匹配“Expression”，属于顺序环视结构，是零宽度的，所以它只参与判断，即使匹配成功，也不会占有字符。

string test = "abc";

Regex reg = new Regex(@"(?(?=a)\w{2}|\w)");

MatchCollection mc = reg.Matches(test);

foreach(Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

ab

c

*/

2.2     平衡组的匹配原理

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";

Regex reg = new Regex(@"$$((?<Open>\()|(?<-Open>$$)|[^()])*(?(Open)(?!))\)");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

Regex reg = new Regex(@"$$#普通字符( ( #分组构造，用来限定量词*修饰范围 (?<Open>\() #命名捕获组，遇到开括弧Open计数加1 | #分支结构 (?<-Open>$$)        #狭义平衡组，遇到闭括弧Open计数减1

|                       #分支结构

[^()]+              #非括弧的其它任意字符

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有Open，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

1.         先找到第一个“(”，作为匹配的开始

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

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

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

5.         最后的“)”，作为匹配的结束

a)          堆栈中Open计数已为0，此时再遇到“)

b)          匹配到字符串结束符

3       平衡组的应用及优化

3.1     单字符嵌套结构平衡组优化

3.1.1  贪婪与非贪婪模式

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";

Regex reg0 = new Regex(@"$$#普通字符“(” ( #分组构造，用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组，遇到开括弧Open计数加1 | #分支结构 (?<-Open>$$)        #狭义平衡组，遇到闭括弧Open计数减1

|                       #分支结构

.                   #任意字符

)*?                     #以上子串出现0次或任意多次，非贪婪模式

(?(Open)(?!))           #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

MatchCollection mc = reg0.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

Regex reg1 = new Regex(@"$$#普通字符“(” ( #分组构造，用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组，遇到开括弧’Open’计数加1 | #分支结构 (?<-Open>$$)        #狭义平衡组，遇到闭括弧’Open’计数减1

|                       #分支结构

[^()]+              #非括弧的其它任意字符

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有’Open’，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

(b*(c+d))/e+f-(g/(h-i))

(b*(c+d))

(g/(h-i))

3.1.2  分支结构

1.         尽量抽象出每个分支中的公共的部分，使最后的表达式中，每个分支共公部分尽可能的少，比如(this|that)的匹配效率是没有th(is|at)高的。

2.         在不影响匹配结果的情况下，把出现概率高的分支放在左侧，出现概率低的分支放右侧。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";

Regex reg2 = new Regex(@"$$#普通字符“(” ( #分组构造，用来限定量词“*”修饰范围 [^()]+ #非括弧的其它任意字符 | #分支结构 (?<Open>\() #命名捕获组，遇到开括弧Open计数加1 | #分支结构 (?<-Open>$$)        #狭义平衡组，遇到闭括弧Open计数减1

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

MatchCollection mc = reg2.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

3.1.3  捕获组

$$(?<Open>)$$(?<-Open>)

(?<Open>)”和“(?<-Open>)”这两种方式只是使用了命名捕获组，捕获的是一个位置，它总是能够匹配成功的，而匹配的内容是空的，分配的内存空间是固定的，可以有效的节省资源，这在单字符嵌套结构中并不明显，但是在字符序列嵌套结构中就比较明显了。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";

Regex reg3 = new Regex(@"$$#普通字符“(” ( #分组构造，用来限定量词“*”修饰范围 [^()]+ #非括弧的其它任意字符 | #分支结构 \( (?<Open>) #命名捕获组，遇到开括弧Open计数加1 | #分支结构$$  (?<-Open>)      #狭义平衡组，遇到闭括弧Open计数减1

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

MatchCollection mc = reg3.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

3.1.4  固化分组

Regex reg = new Regex(@"$$(?>(?<Open>\()|(?<-Open>$$)|.)*?(?(Open)(?!))\)");

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";

Regex reg4 = new Regex(@"$$#普通字符“(” (?> #分组构造，用来限定量词“*”修饰范围 [^()]+ #非括弧的其它任意字符 | #分支结构 \( (?<Open>) #命名捕获组，遇到开括弧Open计数加1 | #分支结构$$  (?<-Open>)      #狭义平衡组，遇到闭括弧Open计数减1

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace);

MatchCollection mc = reg4.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

3.1.5  进一步优化讨论

Regex reg5 = new Regex(@"$$#普通字符“(” (?> #分组构造，用来限定量词“*”修饰范围 [^()]+ #非括弧的其它任意字符 | #分支结构 \( (?<Open>) #命名捕获组，遇到开括弧Open计数加1 | #分支结构$$  (?<-Open>)      #狭义平衡组，遇到闭括弧Open计数减1

)*                      #以上子串出现0次或任意多次

(?(Open)(?!))           #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

\)                          #普通闭括弧

", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);

MatchCollection mc = reg5.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n";

}

/*--------输出--------

(b*(c+d))

(g/(h-i))

*/

3.2     字符序列嵌套结构平衡组应用

<div id="0">

0

</div>

<div id="1">

1

<div id="2">

2

</div>

</div>

3.2.1  提取最外层嵌套结构

string test = @"<div id=""0"">

0

</div>

<div id=""1"">

1

<div id=""2"">

2

</div>

</div>";

Regex reg = new Regex(@"(?isx)                      #匹配模式，忽略大小写，“.”匹配任意字符

<div[^>]*>                      #开始标记“<div...>”

(?>                         #分组构造，用来限定量词“*”修饰范围

<div[^>]*>  (?<Open>)   #命名捕获组，遇到开始标记，入栈，Open计数加1

|                           #分支结构

</div>  (?<-Open>)      #狭义平衡组，遇到结束标记，出栈，Open计数减1

|                           #分支结构

(?:(?!</?div\b).)*      #右侧不为开始或结束标记的任意字符

)*                          #以上子串出现0次或任意多次

(?(Open)(?!))               #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

</div>                          #结束标记“</div>”

");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n--------------------\n";

}

/*--------输出--------

<div id="0">

0

</div>

--------------------

<div id="1">

1

<div id="2">

2

</div>

</div>

--------------------

*/

3.2.2  根据id提取div嵌套标签

string id = Regex.Escape(textBox1.Text);                    //动态获取id

Regex reg = new Regex(@"(?isx)

<div(?:(?!(?:id=|</?div\b)).)*id=(['""]?)" + id  + @"\1[^>]*>        #开始标记“<div...>”

(?>                         #分组构造，用来限定量词“*”修饰范围

<div[^>]*>  (?<Open>)   #命名捕获组，遇到开始标记，入栈，Open计数加1

|                           #分支结构

</div>  (?<-Open>)      #狭义平衡组，遇到结束标记，出栈，Open计数减1

|                           #分支结构

(?:(?!</?div\b).)*      #右侧不为开始或结束标记的任意字符

)*                          #以上子串出现0次或任意多次

(?(Open)(?!))               #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

</div>                          #结束标记“</div>”

");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n--------------------\n";

}

3.2.3  根据id提取任意嵌套标签

string html = @"

<html>

<body>

<div id=""div1"">

<div id=""div2"" style=""background:Red;"">

<div id=""div3"">

<table id=""table1"">

<tr>

<td>

<div id=""div4"" style=""width:100px""></div>

</td>

</tr>

</table>

</div>

</div>

<div id=div5>

<a href=""http://www.csdn.net"">csdn</a>

</div>

</div>

<img src=""http://www.csdn.net/Images/logo_csdn.gif""/>

</body>

</html>";

Console.WriteLine(html);

string[] idList = { "div1", "div2", "div3", "div4", "table1", "div5", "abc(def" };

string pattern = @"<([a-z]+)(?:(?!\bid\b)[^<>])*id=([""']?){0}\2[^>]*>(?><\1[^>]*>(?<o>)|</\1>(?<-o>)|(?:(?!</?\1).)*)*(?(o)(?!))</\1>";

foreach (string id in idList)

{

Match match = Regex.Match(html, string.Format(pattern, Regex.Escape(id)),

RegexOptions.Singleline | RegexOptions.IgnoreCase);

Console.WriteLine("--------begin {0}--------", id);

if (match.Success)

Console.WriteLine(match.Value);

else

Console.WriteLine("o()o");

Console.WriteLine("--------end {0}--------", id);

}

3.2.4  根据标签取外层嵌套结构

string html = @"

<html>

<body>

<div id=""div1"">

<div id=""div2"" style=""background:Red;"">

<div id=""div3"">

<table id=""table1"">

<tr>

<td>

<div id=""div4"" style=""width:100px""></div>

</td>

</tr>

</table>

</div>

</div>

<div id=div5>

<a href=""http://www.csdn.net"">csdn</a>

</div>

</div>

<img src=""http://www.csdn.net/Images/logo_csdn.gif""/>

</body>

</html>";

Console.WriteLine(html);

string[] tagList = { "html", "body", "div", "table", "abc(def" };

string pattern = @"(?isx)

<({0})\b[^>]*>                  #开始标记“<tag...>”

(?>                         #分组构造，用来限定量词“*”修饰范围

<\1[^>]*>  (?<Open>)    #命名捕获组，遇到开始标记，入栈，Open计数加1

|                           #分支结构

</\1>  (?<-Open>)       #狭义平衡组，遇到结束标记，出栈，Open计数减1

|                           #分支结构

(?:(?!</?\1\b).)*       #右侧不为开始或结束标记的任意字符

)*                          #以上子串出现0次或任意多次

(?(Open)(?!))               #判断是否还有'OPEN'，有则说明不配对，什么都不匹配

</\1>                           #结束标记“</tag>”

";

foreach (string tag in tagList)

{

Match match = Regex.Match(html, string.Format(pattern, Regex.Escape(tag)));

Console.WriteLine("--------begin {0}--------", tag);

if (match.Success)

Console.WriteLine(match.Value);

else

Console.WriteLine("o()o");

Console.WriteLine("--------end {0}--------", tag);

}

3.2.5  条件判断结构扩展应用

string test = @"<div id=""0"">

0

</div>

<div id=""1"">

1

<div id=""2"">

2

</div>

</div>";

Regex reg = new Regex(@"(?isx)                              #匹配模式，忽略大小写，“.”匹配任意字符

<div[^>]*>                              #开始标记“<div...>”

(?>                                 #分组构造，用来限定量词“*”修饰范围

|                                   #分支结构

</div>  (?<-Open>)              #遇到结束标记，出栈，Open计数减1

|                                   #分支结构

(?:(?!</?div\b).)*              #右侧不为开始或结束标记的任意字符

)*                                  #以上子串出现0次或任意多次

</div>                                  #结束标记“</div>”

");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n--------------------\n";

}

/*--------输出--------

<div id="1">

1

<div id="2">

2

</div>

</div>

--------------------

*/

string test = @"<div id=""0"">

0

</div>

<div id=""1"">

1

<div id=""2"">

2

</div>

</div>";

Regex reg = new Regex(@"(?is)<div[^>]*>(?:(?!</?div\b).)*</div>");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

richTextBox2.Text += m.Value + "\n--------------------\n";

}

/*--------输出--------

<div id="0">

0

</div>

--------------------

<div id="2">

2

</div>

--------------------

*/

4       平衡组应用范围探讨

Sum(1,Sum(2, Sum(3), 4), 5)

Sum(2, Sum(3), 4)

Sum(3)

Sum(9,Sum(8, Sum(7), 6), 5)

Sum(8, Sum(7), 6)

Sum(7)

//递归方法

private void getNesting(string src, Regex reg, List<string> list)

{

MatchCollection mc = reg.Matches(src);

foreach(Match m in mc)

{

src = m.Value.Remove(m.Value.Length-1, 1);

if (reg.IsMatch(src))

{

getNesting(src, reg, list);

}

}

}

//调用

string test = "1+Sum(1,Sum(2, Sum(3), 4), 5)*4+5+Sum(9,Sum(8, Sum(7), 6), 5)*6+7";

List<string> list = new List<string>();

Regex reg = new Regex(@"(?i)Sum$$(?>[^()]+|\((?<o>)|$$(?<-o>))*(?(o)(?!))\)", RegexOptions.Compiled);

getNesting(test, reg, list);

foreach (string s in list)

{

richTextBox2.Text += s + "\n";

}

string test = @"1+Sum(1,Sum(2, Sum(3), 4), 5)*4+5+Sum(9,Sum(8, Sum(7), 6), 5)*6+7 ";

StringBuilder nesting = new StringBuilder(64);

List<StringBuilder> list = new List<StringBuilder>();

List<string> groups = new List<string>();

int level = 0;

int state = 0;

foreach (char c in test)

{

if ((c == 'S' || c == 's') && state == 0)

{

state = 1;

nesting.Append(c);

}

else if ((c == 'U' || c == 'u') && state == 1)

{

state = 2;

nesting.Append(c);

}

else if ((c == 'M' || c == 'm') && state == 2)

{

state = 3;

nesting.Append(c);

}

else if (c == '(' && state == 3)

{

state = 0;

level++;

}

else

{

state = 0;

nesting = new StringBuilder(64);

}

if (c == ')')

{

if (level > 0)

{

level--;

list.Remove(list[level]);

}

}

if (level > 0)

{

while(list.Count < level)

{

}

for (int i = 0; i < level; i++)

{

list[i].Append(c);

}

}

}

foreach (string s in groups)

{

Console.WriteLine(s);

}