[.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}? |
重複n到m次,但儘可能少重複 |
{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 的行為。該值只能與 IgnoreCase、Multiline 和 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);
==============================================================================================
<如果對你有幫助,記得點一下推薦哦,如有
有不明白或錯誤之處,請多交流>
<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>
<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>
==============================================================================================