欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET正則基礎之平衡組

 更新時間:2023年05月28日 13:50:54   作者:過客  
平衡組是微軟在.NET中提出的一個概念,主要是結合幾種正則語法規(guī)則,提供對配對出現(xiàn)的嵌套結構的匹配,.NET是目前對正則支持最完備、功能最強大的語言平臺之一,而平衡組正是其強大功能的外在表現(xiàn),也是比較實用的文本處理功能

1、概述

平衡組是微軟在.NET中提出的一個概念,主要是結合幾種正則語法規(guī)則,提供對配對出現(xiàn)的嵌套結構的匹配。.NET是目前對正則支持最完備、功能最強大的語言平臺之一,而平衡組正是其強大功能的外在表現(xiàn),也是比較實用的文本處理功能,目前只有.NET支持,相信后續(xù)其它語言會提供支持。

平衡組可以有狹義和廣義兩種定義,狹義平衡組指.NET中定義的(?<Close-Open>Expression)語法,廣義平衡組并不是固定的語法規(guī)則,而是幾種語法規(guī)則的綜合運用,我們平時所說的平衡組通常指的是廣義平衡組。本文中如無特殊說明,平衡組這種簡寫指的是廣義平衡組。

正是由于平衡組功能的強大,所以帶來了一些神秘色彩,其實平衡組并不難掌握。下面就平衡組的匹配原理、應用場景以及性能調(diào)優(yōu)展開討論。

2、平衡組匹配原理

2.1 預備知識

平衡組通常是由量詞,分支結構,命名捕獲組,狹義平衡組,條件判斷結構組成的,量詞和分支結構這里不做介紹,這里只對命名捕獲組,狹義平衡組和條件判斷結構做下說明。

2.1.1命名捕獲組

語法:(?<name>Expression)  

 (?’name’Expression)

以上兩種寫法在.NET中是等價的,都是將“Expression”子表達式匹配到的內(nèi)容,保存到以“name”命名的組里,以供后續(xù)引用。

對于命名捕獲組的應用,這里不做重點介紹,只是需要澄清一點,平時使用捕獲組時,一般反向引用或Group對象使用得比較多,可能會有一種誤解,那就是捕獲組只保留一個匹配結果,即使一個捕獲組可以先后匹配多個子串,也只保留最后一個匹配到的子串。但事實是這樣嗎?

舉例來說:

源字符串:abcdefghijkl

正則表達式:(?<chars>[a-z]{2})+

命名捕獲組chars最終捕獲的是什么?

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";
}
/*--------輸出--------
匹配結果:abcdefghijkl
Group:kl
*/

從m.Groups["chars"].Value的輸出上看,似乎確實是只保留了一個匹配內(nèi)容,但卻忽略了一個事實,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";
? ? ?}
}
/*--------輸出--------
匹配結果:abcdefghijkl
Group:kl
--------------
Capture:ab
Capture:cd
Capture:ef
Capture:gh
Capture:ij
Capture:kl
*/

平時應用時可能會忽略這一點,因為很少遇到一個捕獲組先后匹配多個子串的情況,而在一個捕獲組只匹配一個子串時,Group集合中就只有一個Capture元素,所以內(nèi)容是一樣的。

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";
? ? ?}
}
/*--------輸出--------
匹配結果:ab
Group:ab
--------------
Capture:ab
*/

捕獲組保存的是一個集合,而不只是一個元素,這一知識點對于理解平衡組的匹配原理是有幫助的。

2.1.2狹義平衡組

語法:(?<Close-Open>Expression)

其中“Close”是命名捕獲組的組名,也就是“(?<name>Expression)”中的“name”,可以省略,通常應用時并不關注,所以一般都是省略的,寫作“(?<-Open>Expression)”。作用就是當此處的“Expression”子表達式匹配成功時,則將最近匹配成功到的命名為“Open”組出棧,如果此前不存在匹配成功的“Open”組,那么就報告“(?<-Open>Expression)”匹配失敗,整個表達式在這一位置也是匹配失敗的。

2.1.3條件判斷結構

語法:(?(Expression)yes|no)

      (?(name)yes|no)

對于“(?(Expression)yes|no)”,它是“(?(?=Expression)yes|no)”的簡寫形式,相當于三元運算符

(?=Expression) ? yes : no

表示如果子表達式“(?=Expression)”匹配成功,則匹配“yes”子表達式,否則匹配“no”子表達式。如果“Expression”與可能出現(xiàn)的命名捕獲組的組名相同,為避免混淆,可以采用“(?(?=Expression)yes|no)”方式顯示聲明“Expression”為子表達式,而不是捕獲組名。

“(?=Expression)”驗證當前位置右側是否能夠匹配“Expression”,屬于順序環(huán)視結構,是零寬度的,所以它只參與判斷,即使匹配成功,也不會占有字符。

舉例來說:

源字符串:abc

正則表達式:(?(?=a)\w{2}|\w)

當前位置右側如果是字符“a” ,則匹配兩個“\w”,否則匹配一個“\w”。

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
*/

對于“(?(name)yes|no)”,如果命名捕獲組“name”有捕獲,則匹配“yes”子表達式,否則匹配“no”子表達式。這一語法最典型的一種應用是平衡組。

當然,以上兩種語法中,“yes”和“no都是可以省略的,但同一時間只能省略一個,不能一起省略。平衡組的應用中就是省略了“no”子表達式。

2.2平衡組的匹配原理

平衡組的匹配原理可以用堆棧來解釋,先舉個例子,再根據(jù)例子進行解釋。

源字符串:a+(b*(c+d))/e+f-(g/(h-i))*j

正則表達式:)|[^()])*(?(Open)(?!))\)

需求說明:匹配成對出現(xiàn)的()中的內(nèi)容

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";
Regex reg = new Regex(@")|[^()])*(?(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'計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<-Open>\)) ? ? ? ?#狹義平衡組,遇到閉括弧'Open'計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)0次或任意多次
? ? ? ? ? ? ? ? ? ? ? ? ? ? (?(Open)(?!)) ? ? ? ? ? #判斷是否還有'Open',有則說明不配對,什么都不匹配
? ? ? ? ? ? ? ? ? ? ? ? \) ? ? ? ? ? ? ? ? ? ? ? ? ?#普通閉括弧
? ? ? ? ? ? ? ? ? ? ?", RegexOptions.IgnorePatternWhitespace);

對于一個嵌套結構而言,開始和結束標記都是確定的,對于本例開始為“(”,結束為“)”,那么接下來就是考察中間的結構,中間的字符可以劃分為三類,一類是“(”,一類是“)”,其余的就是除這兩個字符以外的任意字符。

那么平衡組的匹配原理就是這樣的:

1.先找到第一個“(”,作為匹配的開始

2.在第1步以后,每匹配到一個“(”,就入棧一個Open捕獲組,計數(shù)加1

3.在第1步以后,每匹配到一個“)”,就出棧最近入棧的Open捕獲組,計數(shù)減1

4.后面的(?(Open)(?!))用來保證堆棧中Open捕獲組計數(shù)是否為0,也就是“(”和“)”是配對出現(xiàn)的

5.最后的“)”,作為匹配的結束

匹配過程(以下匹配過程,如果覺得難以理解,可以暫時跳過,先學會如何使用,再研究為什么可以這樣用吧)

首先匹配第一個“(”,然后一直匹配,直到出現(xiàn)以下兩種情況之一:

a)堆棧中Open計數(shù)已為0,此時再遇到“)”

b)匹配到字符串結束符

這時控制權交給(?(Open)(?!)),判斷Open是否有匹配,由于此時計數(shù)為0,沒有匹配,那么就匹配“no”分支,由于這個條件判斷結構中沒有“no”分支,所以什么都不做,把控制權交給接下來的“\)”

如果上面遇到的是情況a),那么此時“\)”可以匹配接下來的“\)”,匹配成功;如果上面遇到的是情況b),那么此時會進行回溯,直到“\)”匹配成功為止,否則報告整個表達式匹配失敗。

由于.NET中的狹義平衡組“(?<Close-Open>Expression)”結構,可以動態(tài)的對堆棧中捕獲組進行計數(shù),匹配到一個開始標記,入棧,計數(shù)加1,匹配到一個結束標記,出棧,計數(shù)減1,最后再判斷堆棧中是否還有Open,有則說明開始和結束標記不配對出現(xiàn),不匹配,進行回溯或報告匹配失敗;如果沒有,則說明開始和結束標記配對出現(xiàn),繼續(xù)進行后面子表達式的匹配。

需要對“(?!)”進行一下說明,它屬于順序否定環(huán)視,完整的語法是“(?!Expression)”。由于這里的“Expression”不存在,表示這里不是一個位置,所以試圖嘗試匹配總是失敗的,作用就是在Open不配對出現(xiàn)時,報告匹配失敗。

3、平衡組的應用及優(yōu)化

平衡組提供了嵌套結構的匹配功能,這一創(chuàng)新是很讓人興奮的,因為此前正則對于嵌套結構的匹配是無能為力的。然而功能的強大,自然也帶來了實現(xiàn)的復雜,正則書寫得不好,可能會存在效率陷阱,甚至導致程序崩潰,這里介紹一些基本的優(yōu)化方法。

3.1單字符嵌套結構平衡組優(yōu)化

單字符的嵌套結構指的是開始和結束標記都單個字符的嵌套結構,這種嵌套相對來說比較簡單,優(yōu)化起來也比較容易。先從上面提到的例子開始。

3.1.1貪婪與非貪婪模式

上面給的例子是一種做了部分優(yōu)化的常規(guī)寫法,算作是版本1吧,它做了哪些優(yōu)化呢,先來看下完全沒有做過優(yōu)化的版本0吧。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";
Regex reg0 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? ?#普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? ( ? ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<Open>\() ? ? ? ? #命名捕獲組,遇到開括弧Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<-Open>\)) ? ? ? ?#狹義平衡組,遇到閉括弧Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? . ? ? ? ? ? ? ? ? ? #任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? )*? ? ? ? ? ? ? ? ? ? ? #以上子串出現(xiàn)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))
*/

接下來對比一下版本1。

Regex reg1 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? ?#普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? ( ? ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<Open>\() ? ? ? ? #命名捕獲組,遇到開括弧'Open'計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<-Open>\)) ? ? ? ?#狹義平衡組,遇到閉括弧'Open'計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)0次或任意多次
? ? ? ? ? ? ? ? ? ? ? ? ? ? (?(Open)(?!)) ? ? ? ? ? #判斷是否還有'Open',有則說明不配對,什么都不匹配
? ? ? ? ? ? ? ? ? ? ? ? \) ? ? ? ? ? ? ? ? ? ? ? ? ?#普通閉括弧
? ? ? ? ? ? ? ? ? ? ?", RegexOptions.IgnorePatternWhitespace);

看到區(qū)別了嗎?版本1對版本0的改進主要有兩個地方,一個是用“[^()]+”來代替“.”,另一個是用“*”來代替“*?”,也就是用貪婪模式來代替非貪婪模式。

如果使用了小數(shù)點“.”,那么為什么不能在分組內(nèi)使用“.+”,后面又為什么不能用“*”呢?只要在上面的正則中使用并運行一下代碼就可以知道了,匹配的結果是

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

而不是

(b*(c+d))

(g/(h-i))

因為無論是分組內(nèi)使用“.+”還是后面使用“*”,都是貪婪模式,所以小數(shù)點會一直匹配下去,直到匹配到字符串的結束符才會停止,然后進行回溯匹配。為了取得正確結果,必須使用非貪婪模式“*?”。

這就類似于用“”去匹配“(abc)def(ghi)”一樣,得到的結果是“(abc)def(ghi)”,而不是通常我們希望的“(abc)”和“(ghi)”。這時要用非貪婪模式“”來得到正確的結果。

貪婪模式和非貪婪模式在匹配失敗時,回溯的次數(shù)基本上是一樣的,效率上沒有多大區(qū)別,但是在匹配成功時,貪婪模式比非貪婪模式回溯的次數(shù)要少得多,效率要高得多。

對于“”如果既要得到正確的匹配結果,又要提高匹配效率,可以使用排除型捕獲組+貪婪模式的方式,即“”。

版本0的平衡組也是一樣,可以使用排除字符組“[^()]+”和貪婪模式“*”結合的方式,提高匹配效率,得到的就是版本1的平衡組。

相對于版本0,或許你會認為版本1的寫法是很自然的,但是如果不了解這樣一個演進過程,那么在字符序列嵌套結構平衡組優(yōu)化時,就不會是那么自然的一件事了。

3.1.2  分支結構

接下來就是分支結構的優(yōu)化。

語法:(Exp1|Exp2|Exp3)

因為分支結構的匹配規(guī)則是,從左向右嘗試匹配,當左側分支匹配成功時,就不再向右嘗試。所以使用分支結構時,可以根據(jù)以下兩條規(guī)則進行優(yōu)化:

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

2. 在不影響匹配結果的情況下,把出現(xiàn)概率高的分支放在左側,出現(xiàn)概率低的分支放右側。

對于本例中的分支結構,已經(jīng)沒有公共部分,符合第一條規(guī)則,再看下第二條規(guī)則,開始標記“(”和結束標記“)”出現(xiàn)的概率基本上是一樣的,而除“(”和“)”之外的字符出現(xiàn)的概率是比“(”和“)”出現(xiàn)的概率高的,所以應該把“[^()]+”分支放在左側。

版本1由于采用了排除型捕獲組,所以這三個分支沒有包含關系,左右順序對結果不會造成影響,可以調(diào)整順序。因為這是已經(jīng)經(jīng)過優(yōu)化的了,而如果是版本0,由“.”對“(”和“)”有包含關系,就不能調(diào)整順序了。

在版本1基礎上對分支結構進行優(yōu)化后,就得到版本2。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";
Regex reg2 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? ?#普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? ( ? ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<Open>\() ? ? ? ? #命名捕獲組,遇到開括弧Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?<-Open>\)) ? ? ? ?#狹義平衡組,遇到閉括弧Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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>\))”,而在平衡組的應用中,我是只關心它是否匹配了,而對于匹配到的內(nèi)容是不關心的。對于這樣一種需求,可以用以下方式實現(xiàn)

\( (?<Open>)

\)(?<-Open>)

“(?<Open>)”和“(?<-Open>)”這兩種方式只是使用了命名捕獲組,捕獲的是一個位置,它總是能夠匹配成功的,而匹配的內(nèi)容是空的,分配的內(nèi)存空間是固定的,可以有效的節(jié)省資源,這在單字符嵌套結構中并不明顯,但是在字符序列嵌套結構中就比較明顯了。

由于捕獲組是直接跟在開始或結束標記之后的,所以只要開始或結束標記匹配成功,命名捕獲組自然就會匹配成功,對于功能是沒有任何影響的。

那么把標記和捕獲組調(diào)整一下順序是否可以呢?從功能上來講,是可以的,但是匹配的流程上會有所不同,先是捕獲組匹配成功,入棧,然后再匹配標記,成功則繼續(xù)匹配,不成功則該分支匹配失敗,進行回溯,出棧,繼續(xù)嘗試下一分支。這樣將增加許多入棧和出棧的操作,對匹配效率是有影響的,所以這種方式并不可取。

在版本2基礎上對捕獲組進行優(yōu)化后,就得到版本3。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";
Regex reg3 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? ?#普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? ( ? ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \( ?(?<Open>) ? ? ? #命名捕獲組,遇到開括弧Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \) ?(?<-Open>) ? ? ?#狹義平衡組,遇到閉括弧Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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 固化分組

看到有些人使用平衡組時用到了固化分組,但并不是所有人都明白固化分組的作用。

語法:(?>Expression)

用“”去匹配“(abc)”是可以匹配成功的,因為不用回溯,相對于“”這種非貪婪模式,效率上有所提升,但是對于匹配失敗的情況又如何呢?

源字符串:(abc

正則表達式:

匹配中間過程這里不再詳述,可以參考NFA引擎匹配原理。

當“[^()]+”匹配到結束位置時,控制權交給“\)”,匹配失敗,進行回溯,而由于前面使用了“[^()]+”這種排除型字符組,所以可供回溯的位置,不會存在可以匹配“\)”的情況,這時候的回溯是完全沒有意義的,只會浪費時間,但是由于傳統(tǒng)NFA引擎的特點,必須回溯所有可能之后才會報告匹配失敗。

這時可以用固化分組來進行優(yōu)化,一旦占有字符,就不再釋放。也就是一旦占有,就不再記錄可供回溯的可能。通常是與排除型字符組或順序否定環(huán)視一起使用的。

優(yōu)化后的正則表達式:

需要說明的一點,固化分組要作用于量詞修飾的子表達式才有意義,對于“(?>abc)”由于內(nèi)容是固定的,根本就不會產(chǎn)生回溯,所以使用固化分組是沒有意義的。

對于平衡組的應用也是一樣,如果分組構造中沒有量詞,那么使用固化分組就是沒有意義的,比如版本0

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

這種場景下使用固化分組就是沒有意義的。

在版本3基礎上對捕獲組進行優(yōu)化后,就得到版本4。

string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";
Regex reg4 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? ?#普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? (?> ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \( ?(?<Open>) ? ? ? #命名捕獲組,遇到開括弧Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \) ?(?<-Open>) ? ? ?#狹義平衡組,遇到閉括弧Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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  進一步優(yōu)化討論

那么現(xiàn)在是不是已經(jīng)完成優(yōu)化了呢?是的,通??梢赃@么認為。在一般應用當中,這已經(jīng)是從正則層面上來說,最優(yōu)方案了。

但是在有些場景下,由于Compiled模式可以有效提高分支結構的匹配效率,所以對于源字符串比較復雜的情況,犧牲一些編譯時間和內(nèi)存,還是可以有效提高匹配效率的。

Regex reg5 = new Regex(@"\( ? ? ? ? ? ? ? ? ? ? ? ? #普通字符“(”
? ? ? ? ? ? ? ? ? ? ? ? ? ? (?> ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [^()]+ ? ? ? ? ? ? ?#非括弧的其它任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \( ?(?<Open>) ? ? ? #命名捕獲組,遇到開括弧Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \) ?(?<-Open>) ? ? ?#狹義平衡組,遇到閉括弧Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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))
*/

并不是所有應用場景都適合使用Compiled模式,比如上面這個例子里的源字符串如果是“a+(b*(c+d))/e+f-(g/(h-i))*j”,本身是非常簡單的,使用Compiled模式將是得不償失的。什么時候使用,要根據(jù)具體問題具體分析。

3.2 字符序列嵌套結構平衡組應用

字符序列嵌套結構的匹配,典型的應用就是html標簽的提取。由于上面詳細說明了單字符嵌套結構的優(yōu)化過程,這里主要講應用場景,個別涉及到優(yōu)化的地方再討論。

字符序列嵌套結構的匹配,舉例來說,取div標簽。源字符串如下:

<div id="0">
? ? 0
</div>
<div id="1">
? ? 1
? ? <div id="2">
? ? ? ? 2
</div>
</div>

3.2.1提取最外層嵌套結構

提取最外層div標簽,分析過程及構造方式與單字符嵌套結構差不多,只是捕獲組等內(nèi)容稍稍復雜點,先給出實現(xiàn),再進行解釋。

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計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? </div> ?(?<-Open>) ? ? ?#狹義平衡組,遇到結束標記,出棧,Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?:(?!</?div\b).)* ? ? ?#右側不為開始或結束標記的任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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>
--------------------
*/

在單字符嵌套結構中,使用排除型字符組“[^()]+”,與分組構造外的匹配優(yōu)先量詞“*” 達到貪婪模式匹配效果。在字符序列嵌套結構中,要排除的是一個子串,而不是簡單的幾個無序字符,所以不能使用排除型字符組,此時需要用到順序否定環(huán)視來達到這一目的。“(?:(?!</?div\b).)*”表示的是所在位置右側不是“<div…>”或“</div>”的字符,這樣的字符重復0次或任意多次。關于環(huán)視的細節(jié),可以參考 正則基礎之——環(huán)視。

而由于這種否定環(huán)視包含兩種狀態(tài),所以在與固化分組結合使用時,會與后面的開始或結束標記形成包含關系,所以與固化分組一起使用時,不能放在左側,只能放在右側。

3.2.2  根據(jù)id提取div嵌套標簽

根據(jù)id提取div時,改變的只是最外層div的結構,對內(nèi)分組構造內(nèi)部結構沒有影響。但是因為id是變化的,所以正則需要動態(tài)生成。下面給出實現(xiàn),源字符串和輸出結果由于比較影響篇幅,就不再給出了。

string id = Regex.Escape(textBox1.Text); ? ? ? ? ? ? ? ? ? ?//動態(tài)獲取id
Regex reg = new Regex(@"(?isx)
? ? ? ? ? ? ? ? ? ? ? <div(?:(?!(?:id=|</?div\b)).)*id=(['""]?)" + id ?+ @"\1[^>]*> ? ? ? ?#開始標記“<div...>”
? ? ? ? ? ? ? ? ? ? ? ? ? (?> ? ? ? ? ? ? ? ? ? ? ? ? #分組構造,用來限定量詞“*”修飾范圍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <div[^>]*> ?(?<Open>) ? #命名捕獲組,遇到開始標記,入棧,Open計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? </div> ?(?<-Open>) ? ? ?#狹義平衡組,遇到結束標記,出棧,Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?:(?!</?div\b).)* ? ? ?#右側不為開始或結束標記的任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)0次或任意多次
? ? ? ? ? ? ? ? ? ? ? ? ? (?(Open)(?!)) ? ? ? ? ? ? ? #判斷是否還有'OPEN',有則說明不配對,什么都不匹配
? ? ? ? ? ? ? ? ? ? ? </div> ? ? ? ? ? ? ? ? ? ? ? ? ?#結束標記“</div>”
? ? ? ? ? ? ? ? ? ? ?");
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
? ? ?richTextBox2.Text += m.Value + "\n--------------------\n";
}

在動態(tài)生成正則表達式時,由于輸入的字符串中可能存在正則中有特殊意義的元字符,如果不進行轉義的話,正則解析時會拋出異常。所以用Regex.Escape(string str)來對動態(tài)輸入的字符串進行轉義處理,確保不會因動態(tài)輸入的內(nèi)容而拋異常。比如上面的例子,如果id不進行轉義處理時,輸入“abc(def”就會拋“) 不足”這樣的異常。

3.2.3 根據(jù)id提取任意嵌套標簽

再擴展一下,根據(jù)id屬性取任意嵌套標簽。實現(xiàn)如下,具體實現(xiàn)細節(jié)和討論參考 就是通過id獲得一個html標簽塊。以下正則相對于帖子對個別細節(jié)做了調(diào)整。

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);
}
Console.ReadLine();

3.2.4 根據(jù)標簽取外層嵌套結構

根據(jù)動態(tài)輸入的tag,取相應的最外層的嵌套標簽,實現(xiàn)如下。

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計數(shù)加1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? </\1> ?(?<-Open>) ? ? ? #狹義平衡組,遇到結束標記,出棧,Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?:(?!</?\1\b).)* ? ? ? #右側不為開始或結束標記的任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)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);
}
Console.ReadLine();

3.2.5 條件判斷結構擴展應用

條件判斷結構的作用不只限于驗證開始和結束標記是否配對,根據(jù)需求的不同,還可以有其它一些應用。比如在匹配div標簽時,只取內(nèi)部“存在”嵌套的外層標簽。

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>)(?<Mask>) ?#遇到開始標記,入棧,Open和Mask計數(shù)各加1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? </div> ?(?<-Open>) ? ? ? ? ? ? ?#遇到結束標記,出棧,Open計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #分支結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?:(?!</?div\b).)* ? ? ? ? ? ? ?#右側不為開始或結束標記的任意字符
? ? ? ? ? ? ? ? ? ? ? ? ? )* ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#以上子串出現(xiàn)0次或任意多次
? ? ? ? ? ? ? ? ? ? ? ? ? (?(Open)(?!))(?(Mask)|(?!)) ? ? ? ? #'OPEN'保證標記配對,'Mask'保證內(nèi)部有嵌套
? ? ? ? ? ? ? ? ? ? ? </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>
--------------------
*/

命名捕獲組“(?<Mask>)”只入棧不出棧,如果內(nèi)部有嵌套,則“(?<Mask>)”一定有匹配,此時匹配“(?(Mask)yes|no)”中的“yes”子表達式,也就是什么都不做;如果內(nèi)部沒有嵌套,則“(?<Mask>)”沒有匹配,此時匹配“(?(Mask)yes|no)”中的“no”子表達式,也就是報告匹配失敗。這里省略的是“(?(Mask)yes|no)”中的“yes”子表達式。

對于匹配內(nèi)部沒有嵌套的標簽,也就是最內(nèi)層標簽,可以使用上面的正則表達式,將“(?(Mask)yes|no)”中的“yes”子表達式設為“(?!)”,將“yes”子表達式省略。不過這樣做有些浪費,完全可以用順序否定環(huán)視來實現(xiàn)這一需求。

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、平衡組應用范圍探討

平衡組可以用來匹配嵌套結構,這是一個很大的創(chuàng)新,但是否就認為平衡組適合用來解決任何嵌套問題呢?事實當然不會是這樣。

比如下面這個需求,(參考 請問一個正則表達式) :

源字符串:1+Sum(1,Sum(2, Sum(3), 4), 5)*4+5+Sum(9,Sum(8, Sum(7), 6), 5)*6+7

要求輸出:

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)

這種需求使用平衡組+遞歸的方式可以實現(xiàn),實現(xiàn)代碼如下:

//遞歸方法
private void getNesting(string src, Regex reg, List<string> list)
{
? ? MatchCollection mc = reg.Matches(src);
? ? foreach(Match m in mc)
? ? {
? ? ? ? list.Add(m.Value);
? ? ? ? src = m.Value.Remove(m.Value.Length-1, 1);
? ? ? ? if (reg.IsMatch(src))
? ? ? ? {
? ? ? ? ? ? ?getNesting(src, reg, list);
? ? ? ? }
? ? }
}
//調(diào)用
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)(?!))\)", RegexOptions.Compiled);
getNesting(test, reg, list);
foreach (string s in list)
{
? ? ?richTextBox2.Text += s + "\n";
}

平衡組雖然可以實現(xiàn)要求,但除非你對效率沒有要求,否則這一類需求通常是不適合用正則來實現(xiàn)的。因為平衡組并不是為這一功能而設計的,在實現(xiàn)過程中做了很多額外的嘗試。效率上自然要大打折扣。

類似這樣的需求,可以自己寫有窮自動機來實現(xiàn),畢竟正則也只不過是一種有窮自動機的實現(xià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--;
? ? ? ? ? ? ? ? ? ? ? ? groups.Add(list[level].ToString() + c);
? ? ? ? ? ? ? ? ? ? ? ? list.Remove(list[level]);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (level > 0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? while(list.Count < level)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? list.Add(nesting);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < level; i++)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? list[i].Append(c);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? foreach (string s in groups)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine(s);
? ? ? ? ? ? }
? ? ? ? ? ? Console.ReadLine();

5、其它聲明

到此為止,平衡組的基本應用場景和性能調(diào)優(yōu)都已討論完了,本文對于平衡組匹配原理講得相對比較少,以應用場景分析為主。主要是因為能夠使用平衡組來解決問題的人,通常已經(jīng)對正則的基本語法有了一定程度的理解。而如果事實確實如此,那么對于平衡組的理解,也是水到渠成的了。

以上正則實現(xiàn)中,采用的多是寬松排列模式,主要是為了加注釋,使得閱讀清晰。而寬松排列模式通常用于教學目的,實際使用過程中,如果不是為了可讀性的考慮,可以去掉這些注釋和寬松排列模式參數(shù)。

上面給出了很多平衡組的應用,這里需要說明的是,我提供的只是一些方法和思路,從來不推薦把正則當作模板來用,雖然有些時候,它確實可以當作模板來用,但我還是希望你能真正的掌握這些語法規(guī)則之后,再去應用平衡組。當然,如果你認為能用就行,不需要知道為什么可以這樣用,只是把它當作模板來套,我也無話可說。

到此這篇關于.NET正則基礎之平衡組的文章就介紹到這了,更多相關正則平衡組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論