[.net 物件導向程式設計進階] (3) 正規表示式 (二) 高階應用

yubinfeng發表於2015-06-30

[.net 物件導向程式設計進階] (2) 正規表示式 ()  高階應用

   上一節我們說到了C#使用正規表示式的幾種方法(Replace,Match,Matches,IsMatch,Split等),還有正規表示式的幾種元字元及其應用例項,這些都是學習正規表示式的基礎。本節,我們繼續深入學習表示式的幾種複雜的用法。

 1.分組 

用小括號來指定子表示式(也叫做分組) 

我們通過前一節的學習,知道了重複單個字元,只需要在字元後面加上限定符就可以了, 

比如 a{5},如果要重複多個字元,就要使用小括號分組,然後在後面加上限定符,下面我們看一個示例。 

示例一:重複單字元 和 重複分組字元

//示例:重複單字元 和 重複分組字元
//重複 單個字元
Console.WriteLine("請輸入一個任意字串,測試分組:");
string inputStr = Console.ReadLine();
string strGroup1 = @"a{2}";
Console.WriteLine("單字元重複2兩次替換為22,結果為:"+Regex.Replace(inputStr, strGroup1,"22"));
//重複 多個字元 使用(abcd){n}進行分組限定
string strGroup2 = @"(ab\w{2}){2}";
Console.WriteLine("分組字元重複2兩次替換為5555,結果為:" + Regex.Replace(inputStr, strGroup2, "5555"));

執行結果如下:

示例二:校驗IP4地址(如:192.168.1.4,為四段,每段最多三位,每段最大數字為255,並且第一位不能為0 

//示例:校驗IP4地址(如:192.168.1.4,為四段,每段最多三位,每段最大數字為255,並且第一位不能為0)
string regexStrIp4 = @"^(((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?))$";
Console.WriteLine("請輸入一個IP4地址:");
string inputStrIp4 = Console.ReadLine();
Console.WriteLine(inputStrIp4 + " 是否為合法的IP4地址:" + Regex.IsMatch(inputStrIp4, regexStrIp4));
Console.WriteLine("請輸入一個IP4地址:");
string inputStrIp4Second = Console.ReadLine();           
Console.WriteLine(inputStrIp4 + " 是否為合法的IP4地址:" + Regex.IsMatch(inputStrIp4Second, regexStrIp4));

 執行結果如下:

在這個正規表示式中,我們需要理解 2[0-4]\d|25[0-5]|[01]?\d\d?

這部分表示一個三位數的三種形式

2[0-4]\d     表示:2開頭+0~4的一位數字+任意數

25[0-5]      表示:25開頭+0~5的一位數字

[01]?\d\d?  表示:0或1或空  +  一位任意數字  +  一位任意數或空

這三種形式使用|分開,表示擇一種匹配

理解了這段,整體就理解了,前半部分為 重複三次帶.號,後面一個不帶.號

^ 和 $ 表示匹配整體的開始和結束

2. 後向引用

在這節之前,還是比較好理解的,這段有點難以理解了,但是我們還得拍拍腦袋繼續學習。

先不說後向引用是啥,我們先說,後向引用能幹啥?

前面我們說了分組的概念,就是在正規表示式中使用小括號()完成分組,後向引用就是針對分組的。

如果前面分組以後,後面還要檢索前面分組內同樣的內容,就用到了後向引用,意思就是後面引用前面的分組之意。

…………………………………………………………………容我喘口氣…………………………………………………………………………………………

那麼要如何在後面引用前面分組呢?

這裡就需要有一個組名,即定義一個組名,在後面引用。

在定義組名前,說一下

則對於分組的工作原理:

A.分組後,正則會自動為每個分組編一個組號

B.分配組號時,從左往右掃描兩遍,第一遍給未命名分組分配組號,第二遍自然是給命名組分配組號。因此,命名組的組號是大於未命名組的組號。(說這些工作原理,對我們使用後向引用沒有什麼作用,就是理解一下,內部工作原理而已)

C.也不是所有的組都會分配組號,我們可以使用(:?exp)來禁止分配組號。 

不知道小夥伴們是否看明白了,至少我看了網上關於這塊兒的介紹,都不是很清晰。

下面先看一下後向引用 捕獲 的定義

分類

程式碼/語法

說明

捕獲

(exp)

匹配exp,並捕獲文字到自動命名的組裡

(?<name>exp)

匹配exp,並捕獲文字到名稱為name的組裡,也可以寫成(?'name'exp)

(?:exp)

匹配exp,不捕獲匹配的文字,也不給此分組分配組號

 先來對上面的三個語法理解一下:

(exp) 就是分組,使用() ,exp就是分組內的子表示式 ,這個在上面分組中已經說明了

(?<name>exp)或 (?’name’exp) 就是組分組命名,只是有兩種寫法而已

(?:exp)exp表示式不會被捕獲到某個組裡,也不會分配組號,但不會改變正則處理方式,肯定很多小盆友要問,為啥要有這個東西,要他何用?

大家看一下下面這個示例:

^([1-9][0-9]*|0)$  表示匹配0-9的整數

再看下面這個

^(?:[1-9][0-9]*|0)$ 也是匹配0-9的整數,但是使用了非捕獲組

作用呢?兩個字:效率

因為我們定義了組,但是我們只是為了使用組來限定範圍開始和結束,並不是為了後向引用,因此使用非捕獲組來禁上分配組號和儲存到組裡,極大的提高了檢索效率。

有了上面的理解,我才好寫點示例,先理論後實踐。 

在進行示例前,我們先複習以下上節學到的元字元

   *    匹配前面的子表示式任意次。例如,zo* 能匹配“z”,“zo”以及“zoo”。*等價於{ 0,}。

   +    匹配前面的子表示式一次或多次(大於等於1次)。例如,“zo +”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價於{ 1,}。

   ?    匹配前面的子表示式零次或一次。例如,“do (es) ?”可以匹配“do”或“does”中的“do”。?等價於{ 0,1}。  

   \w  匹配包括下劃線的任何單詞字元。類似但不等價於“[A-Za-z0-9_]”,這裡的"單詞"字元使用Unicode字符集。

   \b  匹配一個單詞邊界,也就是指單詞和空格間的位置(即正規表示式的“匹配”有兩種概念,一種是匹配字元,一種是匹配位置,這裡的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。       

   \s  匹配任何不可見字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。

//後向引用

//在後向引用前,我們先熟悉一下單詞的查詢  
//複習一下前面學的幾個元字元
// * 匹配前面的子表示式任意次。例如,zo* 能匹配“z”,“zo”以及“zoo”。*等價於{ 0,}。
// + 匹配前面的子表示式一次或多次(大於等於1次)。例如,“zo +”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價於{ 1,}。
// ? 匹配前面的子表示式零次或一次。例如,“do (es) ?”可以匹配“do”或“does”中的“do”。?等價於{ 0,1}。  
// \w 匹配包括下劃線的任何單詞字元。類似但不等價於“[A-Za-z0-9_]”,這裡的"單詞"字元使用Unicode字符集。
// \b 匹配一個單詞邊界,也就是指單詞和空格間的位置(即正規表示式的“匹配”有兩種概念,一種是匹配字元,一種是匹配位置,這裡的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。       
// \s 匹配任何不可見字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。
string str1 = "zoo zoo hello how you mess ok ok home house miss miss yellow";

//示例一:查詢單詞中以e結尾的,並將單詞替換為了*
string regexStr1 = @"\w+e\b";
Console.WriteLine("示例一:" + Regex.Replace(str1, regexStr1,"*"));

//示例二:查詢單詞中以h開頭的,並將單詞替換為#
string regexStr2 = @"\bh\w+";
Console.WriteLine("示例二:" + Regex.Replace(str1, regexStr2, "#"));

//示例三:查詢單詞中有重疊ll的單詞,將單詞替換為@
string regexStr3 = @"\b\w+l+\w+\b";
Console.WriteLine("示例三:" + Regex.Replace(str1, regexStr3, "@"));

此外,我們說到了分組及編號,那麼如何取到某個分組的匹配文字呢?

使用 \1 表示取第一個分組的匹配文字 

//使用 \1 代表第一個分組 組號為1的匹配文字
//示例四:查詢單詞中有兩個重複的單詞,替換為%  
string regexStr4 = @"(\b\w+\b)\s+\1";
Console.WriteLine("示例四:" + Regex.Replace(str1, regexStr4, "%"));
//上面示例中,第一個分組匹配任意一個單詞 \s+ 表示任意空字元 \1 表示匹配和第一個分組相同

 別名定義 (?<name>exp)  ,別名後向引用 \k<name> 

//使用別名 代替 組號 ,別名定義 (?<name>exp)  ,別名後向引用 \k<name>
//示例五:使用別名後向引用 查詢 查詢單詞中有兩個重複的單詞,替換為%%  
string regexStr5 = @"(?<myRegWord>\b\w+\b)\s+\k<myRegWord>";
Console.WriteLine("示例五:"+Regex.Replace(str1, regexStr5, "%%"));

 執行結果如下:

3. 零寬斷言

什麼!?什麼!?對你沒看錯,這段題目叫“零寬斷言”(頓時1W頭艹尼馬奔騰而過!!!!)

本來正規表示式學到本節,小夥伴們漸漸感覺看著有點吃力了,還來這麼一個名字。

不過,不要忘記,我們這一系列的文章叫“.NET 進階”既然是進階,自然就有很多有難度的問題要解決,我們還是先看一下這個概念的意思吧!

什麼是零寬斷言?

我們有經常要查詢某些文字某個位置前面或後面的東西,就像 ^  $  \b一樣,指定一個位置,這個位置需要滿足一定的條件(斷言),我們把這個稱做 零寬斷言 

分類

程式碼/語法

說明

零寬斷言

(?=exp)

匹配exp前面的位置

(?<=exp)

匹配exp後面的位置

(?!exp)

匹配後面跟的不是exp的位置

(?<!exp)

匹配前面不是exp的位置

看上面的表,零寬斷言一共有四個型別的位置判斷寫法

我們先舉幾個示例來看看,TMD零寬斷言到底解決了什麼問題?

//零寬斷言
//示例一:將下面字串每三位使用逗號分開
string stringFist = "dfalrewqrqwerl43242342342243434abccc";
string regexStringFist = @"((?=\w)\w{3})";
string newStrFist = String.Empty;
Regex.Matches(stringFist, regexStringFist).Cast<Match>().Select(m=>m.Value).ToList<string>().ForEach(m => newStrFist += (m+","));
Console.WriteLine("示例一:" + newStrFist.TrimEnd(','));

//示例二:查詢字串中,兩個空格之間的所有數字
string FindNumberSecond = "asdfas 3 dfasfas3434324 9 8888888 7 dsafasd342";
string regexFindNumberSecond = @"((?<=\s)\d(?=\s))";
string newFindNumberSecond = String.Empty;
Regex.Matches(FindNumberSecond, regexFindNumberSecond).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newFindNumberSecond += (m + " "));
Console.WriteLine("示例二:" + newFindNumberSecond);

執行結果如下:

?=exp)也叫零寬度正預測先行斷言,它斷言自身出現的位置的後面能匹配表示式exp。

(?<=exp)也叫零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表示式exp。

4.  負向零寬斷言

實在無語了,零寬斷言剛理解完,又來了一個負向零寬斷言,真是非常的坑爹。

解釋這個概念已經非常無力了,除非你不是地球人。但這個東東本身並不難使用,我們直接解決一個問題。

//負向零寬斷言
//示例:查詢單詞中包含x並且x前面不是o的所有單詞
string strThird = "hot example how box house ox xerox his fox six my";
string regexStrThird = @"\b\w*[^o]x\w*\b";
string newStrThird = String.Empty;
Regex.Matches(strThird, regexStrThird).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird += (m + " "));
Console.WriteLine("示例一:" + newStrThird);
//我們發現以上寫法,如果以x開頭的單詞,就會出錯,原因是[^o]必須要匹配一個非o的字元
//為了解決以上問題,我們需要使用負向零寬斷言 (?<!o])負向零寬斷言能解決這樣的問題,因為它只匹配一個位置,並不消費任何字元
//改進以後如下
string regexStrThird2 = @"\b\w*(?<!o)x\w*\b";
string newStrThird2 = String.Empty;
Regex.Matches(strThird, regexStrThird2).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird2 += (m + " "));
Console.WriteLine("示例二:" + newStrThird2);

//示例三:如查詢上面示例,但是要求區配必須含o但 後面不能有x 則要使用 (?!x)
string regexStrThird3 = @"\b\w*o(?!x)\w*\b";
string newStrThird3 = String.Empty;
Regex.Matches(strThird, regexStrThird3).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird3 += (m + " "));
Console.WriteLine("示例三:" + newStrThird3);

執行結果如下:

再舉一個示例:

//例項一:取到<div name='Fist'></div>以內的內容   使用零寬斷言 正負斷言就可以解決了
//以下為模擬一個HTML表格資料
StringBuilder strHeroes =new StringBuilder();
strHeroes.Append("<table>");
strHeroes.Append("<tr><td>武林高手</td></tr>");
strHeroes.Append("<tr><td>");
strHeroes.Append("<div name='Fist'>");
strHeroes.Append("<div>歐陽鋒</div>");
strHeroes.Append("<div>白駝山</div>");
strHeroes.Append("<div>蛤蟆功</div>");
strHeroes.Append("</div>");

strHeroes.Append("<div>");
strHeroes.Append("<div>黃藥師</div>");
strHeroes.Append("<div>桃花島</div>");
strHeroes.Append("<div>彈指神通</div>");
strHeroes.Append("</div>");

strHeroes.Append("</td></tr>");
strHeroes.Append("</table>");
            
Console.WriteLine("原字串:"+strHeroes.ToString());
          
string newTr = String.Empty;
string regexTr = @"(?<=<div name='Fist'>).+(?=</div>)";
Regex.Matches(strHeroes.ToString(),regexTr).Cast
<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newTr += (m + "\n")); Console.WriteLine(Regex.IsMatch(strHeroes.ToString(), regexTr)); Console.WriteLine("例項一:"+newTr);

執行結果如下:

5. 註釋

正規表示式很長的時候,很難讀懂,因些他有專門的註釋方法,主要有兩種寫法

A.通過語法(?#comment) 

例如:\b\w*o(?!x)(?#o後面不包含x)\w*\b  紅色部分是註釋,他不對錶達式產生任何影響

B.啟用忽略模式裡的空白符選項的多行註釋法

例如:

//正規表示式註釋
//重寫上面的例子,採用多行註釋法
string regexStrThird4 = @"\b         #限定單詞開頭
                          \w*        #任意長度字母數字及下劃線
                          o(?!x)     #含o字母並且後面的一個字母不是x
                          \w*        #任意長度字母數字及下劃線
                          \b         #限定單詞結尾
                          ";
string newStrThird4 = String.Empty;
Regex.Matches(strThird, regexStrThird4,RegexOptions.IgnorePatternWhitespace).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird4 += (m + " "));
Console.WriteLine("示例四:" + newStrThird4);

執行結果同上。

6. 註釋貪婪與懶惰

這個比較好理解,首先,懶惰是針對重複匹配模式來說的,我們先看一下下面的表 懶惰的限定符如下:

懶惰限定符

程式碼/語法

說明

*?

重複任意次,但儘可能少重複

+?

重複1次或更多次,但儘可能少重複

??

重複0次或1次,但儘可能少重複

{n,m}?

重複nm次,但儘可能少重複

{n,}?

重複n次以上,但儘可能少重複

 通過示例來看

//懶惰限定符
string LazyStr = "xxyxy";
string regexLazyStr = @"x.*y";
string regexLazyStr2 = @"x.*?y";
string newLazyStr = String.Empty;
Regex.Matches(LazyStr, regexLazyStr).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newLazyStr += (m + " "));
string newLazyStr2 = String.Empty;
Regex.Matches(LazyStr, regexLazyStr2).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newLazyStr2 += (m + " "));
Console.WriteLine("貪婪模式:" + newLazyStr);
Console.WriteLine("懶惰模式:" + newLazyStr2);

執行結果如下:

我們可以看出:

懶惰匹配,也就是匹配儘可能少的字元。

相反,貪婪模式則儘可能多的匹配字元

貪婪模式都可以被轉化為懶惰匹配模式,只要在它後面加上一個問號? 

7.處理與選項

常用的處理選項

名稱

說明

IgnoreCase(忽略大小寫)

匹配時不區分大小寫。

Multiline(多行模式)

更改^和$的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個字串的開頭和結尾匹配。(在此模式下,$的精確含意是:匹配\n之前的位置以及字串結束前的位置.)

Singleline(單行模式)

更改.的含義,使它與每一個字元匹配(包括換行符\n)。

IgnorePatternWhitespace(忽略空白)

忽略表示式中的非轉義空白並啟用由#標記的註釋。

ExplicitCapture(顯式捕獲)

僅捕獲已被顯式命名的組。

這個我們在前面已經很多次用到了,比如 註釋的示例。 

.NET 正規表示式中,我們使用RegexOptions 列舉來完成。

上一節在說到.NET常用正規表示式方法時簡單作了說明,下面我們把.NET RegexOptions 列舉的各個值列舉說明一下

提供用於設定正規表示式選項的列舉值。此列舉有一個 FlagsAttribute 屬性,允許其成員值按位組合

Compiled:指定將正規表示式編譯為程式集。這會產生更快的執行速度,但會增加啟動時間。

CultureInvariant:指定忽略語言中的區域性差異。有關更多資訊,請參見 在 RegularExpressions 名稱空間中執行不區分割槽域性的操作

ECMAScript:為表示式啟用符合 ECMAScript 的行為。該值只能與 IgnoreCaseMultiline 和 Compiled 值一起使用。該值與其他任何值一起使用均將導致異常。

ExplicitCapture:指定有效的捕獲僅為形式為 (?<name>...) 的顯式命名或編號的組。這使未命名的圓括號可以充當非捕獲組,並且不會使表示式的語法 (?:...) 顯得笨拙。

IgnoreCase:指定不區分大小寫的匹配。

IgnorePatternWhitespace:消除模式中的非轉義空白並啟用由 # 標記的註釋。但是,IgnorePatternWhitespace 值不會影響或消除字元類中的空白。

Multiline:多行模式。更改 ^ 和 $ 的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個字串的開頭和結尾匹配。

None:指定不設定選項。

RightToLeft:指定搜尋從右向左而不是從左向右進行。

Singleline:指定單行模式。更改點 (.) 的含義,使它與每一個字元匹配(而不是與除 /n 之外的每個字元匹配)。

示例:

//處理選項
//Compiled表示編譯為程式集,使效率提高   
//IgnoreCase表示匹配不分大小寫
Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b",RegexOptions.Compiled | RegexOptions.IgnoreCase);
string myWords = "hello hello How Hi hi are you you apple o o ";
string newMyWords = String.Empty;
rx.Matches(myWords).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newMyWords += (m + ","));
Console.WriteLine("相同的單詞有:" + newMyWords.Trim(','));

執行結果如下:

8. 判斷表示式 

     這裡需要用到以下的語法構造:

  • (?'group') 把捕獲的內容命名為group,並壓入堆疊(Stack)
  • (?'-group') 從堆疊上彈出最後壓入堆疊的名為group的捕獲內容,如果堆疊本來為空,則本分組的匹配失敗
  • (?(group)yes|no) 如果堆疊上存在以名為group的捕獲內容的話,繼續匹配yes部分的表示式,否則繼續匹配no部分
  • (?!) 零寬負向先行斷言,由於沒有字尾表示式,試圖匹配總是失敗 

   可以看到,實際上就是一個除|擇一之後的,另一種判斷表達,這裡算是個總結 ,下面幾種寫法帶有判斷性質語法:

     (1)、A|B,這個是最基本的,A或者B,其實這個不能算判斷
     (2)、(?(expression)yes-expression|no-expression),其中no-expression為可選項,意為,如果expression成立,則要求匹配yes-expression,否則要求匹配no-expression
     (3)、(?(group-name)yes-expressioin|no-expression),其中no-expression為可選項,意為,如果名為group-name的組匹配成功,則要求匹配yes-expression,否則要求匹配no-expression
     判斷表示式還是很好理解的,唯有一點要注意:@"(?(A)A|B)"不能匹配"AA",我們應該這樣寫Regex: @”(?(A)AA|B)”,請注意,判斷式中的內容並不會做為yes-expression或no-expression表示式的一部分。 

      (?'group') 和(?'group') 通常用作平衡組匹配,比如我們匹配html等對稱標籤時,對於不對稱的標籤,則匹配失敗  

本節所有原始碼貼上來 

  1 //示例:重複單字元 和 重複分組字元
  2 //重複 單個字元
  3 Console.WriteLine("請輸入一個任意字串,測試分組:");
  4 string inputStr = Console.ReadLine();
  5 string strGroup1 = @"a{2}";
  6 Console.WriteLine("單字元重複2兩次替換為22,結果為:"+Regex.Replace(inputStr, strGroup1,"22"));
  7 //重複 多個字元 使用(abcd){n}進行分組限定
  8 string strGroup2 = @"(ab\w{2}){2}";
  9 Console.WriteLine("分組字元重複2兩次替換為5555,結果為:" + Regex.Replace(inputStr, strGroup2, "5555"));
 10 
 11 Console.WriteLine("\n");
 12 
 13 //示例:校驗IP4地址(如:192.168.1.4,為四段,每段最多三位,每段最大數字為255,並且第一位不能為0)
 14 string regexStrIp4 = @"^(((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?))$";
 15 Console.WriteLine("請輸入一個IP4地址:");
 16 string inputStrIp4 = Console.ReadLine();
 17 Console.WriteLine(inputStrIp4 + " 是否為合法的IP4地址:" + Regex.IsMatch(inputStrIp4, regexStrIp4));
 18 Console.WriteLine("請輸入一個IP4地址:");
 19 string inputStrIp4Second = Console.ReadLine();           
 20 Console.WriteLine(inputStrIp4 + " 是否為合法的IP4地址:" + Regex.IsMatch(inputStrIp4Second, regexStrIp4));
 21            
 22 //後向引用
 23 
 24 //在後向引用前,我們先熟悉一下單詞的查詢  
 25 //複習一下前面學的幾個元字元
 26 // * 匹配前面的子表示式任意次。例如,zo* 能匹配“z”,“zo”以及“zoo”。*等價於{ 0,}。
 27 // + 匹配前面的子表示式一次或多次(大於等於1次)。例如,“zo +”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價於{ 1,}。
 28 // ? 匹配前面的子表示式零次或一次。例如,“do (es) ?”可以匹配“do”或“does”中的“do”。?等價於{ 0,1}。  
 29 // \w 匹配包括下劃線的任何單詞字元。類似但不等價於“[A-Za-z0-9_]”,這裡的"單詞"字元使用Unicode字符集。
 30 // \b 匹配一個單詞邊界,也就是指單詞和空格間的位置(即正規表示式的“匹配”有兩種概念,一種是匹配字元,一種是匹配位置,這裡的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。       
 31 // \s 匹配任何不可見字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。
 32 string str1 = "zoo zoo hello how you mess ok ok home house miss miss yellow";
 33 
 34 //示例一:查詢單詞中以e結尾的,並將單詞替換為了*
 35 string regexStr1 = @"\w+e\b";
 36 Console.WriteLine("示例一:" + Regex.Replace(str1, regexStr1,"*"));
 37 
 38 //示例二:查詢單詞中以h開頭的,並將單詞替換為#
 39 string regexStr2 = @"\bh\w+";
 40 Console.WriteLine("示例二:" + Regex.Replace(str1, regexStr2, "#"));
 41 
 42 //示例三:查詢單詞中有重疊ll的單詞,將單詞替換為@
 43 string regexStr3 = @"\b\w+l+\w+\b";
 44 Console.WriteLine("示例三:" + Regex.Replace(str1, regexStr3, "@"));
 45 
 46 //使用 \1 代表第一個分組 組號為1的匹配文字
 47 //示例四:查詢單詞中有兩個重複的單詞,替換為%  
 48 string regexStr4 = @"(\b\w+\b)\s+\1";
 49 Console.WriteLine("示例四:" + Regex.Replace(str1, regexStr4, "%"));
 50 //上面示例中,第一個分組匹配任意一個單詞 \s+ 表示任意空字元 \1 表示匹配和第一個分組相同
 51 
 52 //使用別名 代替 組號 ,別名定義 (?<name>exp)  ,別名後向引用 \w<name>
 53 //示例五:使用別名後向引用 查詢 查詢單詞中有兩個重複的單詞,替換為%%  
 54 string regexStr5 = @"(?<myRegWord>\b\w+\b)\s+\k<myRegWord>";
 55 Console.WriteLine("示例五:"+Regex.Replace(str1, regexStr5, "%%"));
 56 
 57 //零寬斷言
 58 //示例一:將下面字串每三位使用逗號分開
 59 string stringFist = "dfalrewqrqwerl43242342342243434abccc";
 60 string regexStringFist = @"((?=\w)\w{3})";
 61 string newStrFist = String.Empty;
 62 Regex.Matches(stringFist, regexStringFist).Cast<Match>().Select(m=>m.Value).ToList<string>().ForEach(m => newStrFist += (m+","));
 63 Console.WriteLine("示例一:" + newStrFist.TrimEnd(','));
 64 
 65 //示例二:查詢字串中,兩個空格之間的所有數字
 66 string FindNumberSecond = "asdfas 3 dfasfas3434324 9 8888888 7 dsafasd342";
 67 string regexFindNumberSecond = @"((?<=\s)\d(?=\s))";
 68 string newFindNumberSecond = String.Empty;
 69 Regex.Matches(FindNumberSecond, regexFindNumberSecond).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newFindNumberSecond += (m + " "));
 70 Console.WriteLine("示例二:" + newFindNumberSecond);
 71 
 72 //負向零寬斷言
 73 //示例:查詢單詞中包含x並且x前面不是o的所有單詞
 74 string strThird = "hot example how box house ox xerox his fox six my";
 75 string regexStrThird = @"\b\w*[^o]x\w*\b";
 76 string newStrThird = String.Empty;
 77 Regex.Matches(strThird, regexStrThird).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird += (m + " "));
 78 Console.WriteLine("示例一:" + newStrThird);
 79 //我們發現以上寫法,如果以x開頭的單詞,就會出錯,原因是[^o]必須要匹配一個非o的字元
 80 //為了解決以上問題,我們需要使用負向零寬斷言 (?<!o])負向零寬斷言能解決這樣的問題,因為它只匹配一個位置,並不消費任何字元
 81 //改進以後如下
 82 string regexStrThird2 = @"\b\w*(?<!o)x\w*\b";
 83 string newStrThird2 = String.Empty;
 84 Regex.Matches(strThird, regexStrThird2).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird2 += (m + " "));
 85 Console.WriteLine("示例二:" + newStrThird2);
 86 
 87 //示例三:如查詢上面示例,但是要求區配必須含o但 後面不能有x 則要使用 (?!x)
 88 string regexStrThird3 = @"\b\w*o(?!x)\w*\b";
 89 string newStrThird3 = String.Empty;
 90 Regex.Matches(strThird, regexStrThird3).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird3 += (m + " "));
 91 Console.WriteLine("示例三:" + newStrThird3);
 92 
 93 //正規表示式註釋
 94 //重寫上面的例子,採用多行註釋法
 95 string regexStrThird4 = @"  \b         #限定單詞開頭
 96                             \w*        #任意長度字母數字及下劃線
 97                             o(?!x)     #含o字母並且後面的一個字母不是x
 98                             \w*        #任意長度字母數字及下劃線
 99                             \b         #限定單詞結尾
100                             ";
101 string newStrThird4 = String.Empty;
102 Regex.Matches(strThird, regexStrThird4,RegexOptions.IgnorePatternWhitespace).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newStrThird4 += (m + " "));
103 Console.WriteLine("示例四:" + newStrThird4);
104 
105 //懶惰限定符
106 string LazyStr = "xxyxy";
107 string regexLazyStr = @"x.*y";
108 string regexLazyStr2 = @"x.*?y";
109 string newLazyStr = String.Empty;
110 Regex.Matches(LazyStr, regexLazyStr).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newLazyStr += (m + " "));
111 string newLazyStr2 = String.Empty;
112 Regex.Matches(LazyStr, regexLazyStr2).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newLazyStr2 += (m + " "));
113 Console.WriteLine("貪婪模式:" + newLazyStr);
114 Console.WriteLine("懶惰模式:" + newLazyStr2);               
115 
116 //處理選項
117 //Compiled表示編譯為程式集,使效率提高   
118 //IgnoreCase表示匹配不分大小寫
119 Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b",RegexOptions.Compiled | RegexOptions.IgnoreCase);
120 string myWords = "hello hello How Hi hi are you you apple o o ";
121 string newMyWords = String.Empty;
122 rx.Matches(myWords).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newMyWords += (m + ","));
123 Console.WriteLine("相同的單詞有:" + newMyWords.Trim(','));
124 
125 //例項一:取到<div name='Fist'></div>以內的內容   使用零寬斷言 正負斷言就可以解決了
126 //以下為模擬一個HTML表格資料
127 StringBuilder strHeroes =new StringBuilder();
128 strHeroes.Append("<table>");
129 strHeroes.Append("<tr><td>武林高手</td></tr>");
130 strHeroes.Append("<tr><td>");
131 strHeroes.Append("<div name='Fist'>");
132 strHeroes.Append("<div>歐陽鋒</div>");
133 strHeroes.Append("<div>白駝山</div>");
134 strHeroes.Append("<div>蛤蟆功</div>");
135 strHeroes.Append("</div>");
136 
137 strHeroes.Append("<div>");
138 strHeroes.Append("<div>黃藥師</div>");
139 strHeroes.Append("<div>桃花島</div>");
140 strHeroes.Append("<div>彈指神通</div>");
141 strHeroes.Append("</div>");
142 
143 strHeroes.Append("</td></tr>");
144 strHeroes.Append("</table>");
145             
146 Console.WriteLine("原字串:"+strHeroes.ToString());
147           
148 string newTr = String.Empty;
149 string regexTr = @"(?<=<div name='Fist'>).+(?=</div>)";
150     Regex.Matches(strHeroes.ToString(),regexTr).Cast<Match>().Select(m => m.Value).ToList<string>().ForEach(m => newTr += (m + "\n"));
151 Console.WriteLine(Regex.IsMatch(strHeroes.ToString(), regexTr));
152 Console.WriteLine("例項一:"+newTr);
View Code

==============================================================================================  

 返回目錄

 <如果對你有幫助,記得點一下推薦哦,如有

有不明白或錯誤之處,請多交流>  

<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>

<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>

.NET 技術交流群:467189533    .NET 程式設計

==============================================================================================   

相關文章