日常工作中,總會遇到正則的時候,索性就把它 搞清楚。后來才發現正則很好用,完全可以替代split和repleace的那種需要循環遍歷時的無賴。
簡單表達式
最簡單的正則表達式大家都已熟悉,即文字字符串。特定的字符串可通過文字本身加以描述;像 foo 這樣的正則表達式模式可精確匹配輸入的字符串 foo。在本例中,也將匹配如下輸入:The foo d was quite tasty,如果希望精確匹配,這可能不是預期結果。
當然,使用正則表達式匹配等於它自身的精確字符串是沒有價值的實現,不能體現正則表達式的真正作用。假如不查找 foo,而是查找以字母 f 開頭的所有單詞,或所有 3 個字母的單詞,那該怎么辦?目前,這超出了文字字符串的合理范圍。我們需要更加深入地研究正則表達式。下面是一個文字表達式示例及一些匹配的輸入。
| 模式 |
輸入(匹配) |
|---|---|
| foo |
foo、food、foot、“There's evil afoot.” |
限定符
限定符提供了一種簡單方法,用於指定在模式中允許特定字符或字符集自身重復出現的次數。有 3 個非顯式限定符:
-
*,描述“出現 0 或多次”。
-
+,描述“出現 1 或多次”。
-
?,描述“出現 0 或 1 次”。
限定符始終引用限定符前(左邊)的模式,通常是單個字符,除非使用括號創建模式組。下面是一些模式示例及匹配的輸入。
| 模式 |
輸入(匹配) |
|---|---|
| fo* |
foo、foe、food、fooot、“forget it”、funny、 puffy |
| fo+ |
foo、foe、food、foot、“forget it” |
| fo? |
foo、foe、food、foot、“forget it”、funny、puffy |
除了指定給定模式准確出現 0 或 1 次之外,? 字符還可強制模式或子模式匹配數目最少的字符(如果匹配輸入字符串中的多個字符)。
除了非顯式限定符(一般叫做限定符,但為區別於下一組,故稱非顯式限定符)之外,還有顯式限定符。在模式出現次數方面,限定符的概念非常模糊。使用 顯式限定符則可准確指定數字、范圍或數字集。顯式限定符位於所應用的模式的后邊,這一點與正則限定符一樣。顯式限定符使用花括號 {} 及其中的數字值表示模式出現次數的上下限【下圖,第一項,代表至少有兩個字母aa,第三項代表至少有二個bb,之多3個】。例如,x{5} 將准確匹配 5 個 x 字符 (xxxxx)。如果僅指定一個數字,則表示次數上限;如果數字后跟一個逗號,如 x{5,},表示匹配任何出現次數大於 4 的 x 字符。下面是一些模式示例及匹配的輸入。
| 模式 |
輸入(匹配) |
|---|---|
| ab{2}c |
abbc、aaabbccc |
| ab{,2}c |
ac、abc、abbc、aabbcc |
| ab{2,3}c |
abbc、abbbc、aabbcc、aabbbcc |
第二個a{,2}驗證未通過,應該改為ab{0,2}c
| 模式 |
輸入(匹配) |
|---|---|
| fo* |
foo、foe、food、fooot、“forget it”、funny、 puffy |
| fo+ |
foo、foe、food、foot、“forget it” |
| fo? |
foo、foe、food、foot、“forget it”、funny、puffy |
正則表達式選項RegexOptions有如下一下選項,詳細說明請參考聯機幫助
| RegexOptions枚舉值 |
內聯標志 |
簡單說明 |
| ExplicitCapture |
n |
只有定義了命名或編號的組才捕獲 |
| IgnoreCase |
i |
不區分大小寫 |
| IgnorePatternWhitespace |
x |
消除模式中的非轉義空白並啟用由 # 標記的注釋。 |
| MultiLine |
m |
多行模式,其原理是修改了^和$的含義 |
| SingleLine |
s |
單行模式,和MultiLine相對應 |
這里我提到內聯標志,是因為相對於用RegexOptions在new Regex時定義Regex表達式的全局選項來說,內聯標志可以更小粒度(以組為單位)的定義匹配選項,從而更方便表達我們的思想
語法是這樣的:(?i:expression)為定義一個選項,(?-i:expression)為刪除一個選項,(?i-s:expression)則定義i,刪除s,是的,我們可以一次定義很多個選項。這樣,通過內聯選項,你就可以在一個Regex中定義一個組為匹分大小寫的,一個組不匹分大小寫的。
元字符
在正則表達式中,有一種意義特殊的構造,即元字符。目前已知的元字符有很多,如 *、?、+ 和 {} 字符。其他字符在正則表達式語言中都有特殊的含義。這些字符包括:$ ^ . [ ( | ) ] 和 /。
.(句點或點)元字符是最簡單但最常用的一個字符。它可匹配任何單字符。如果要指定某些模式可包含任意組合的字 符,使用句點非常有用,但一定要在特定長度范圍內。此外,我們知道表達式將對包含在較長字符串中的所有模式進行匹配,假如只需要精確匹配模式,又該怎么 辦?這在驗證方案中經常出現,例如,要確保用戶輸入的郵政編碼或電話號碼的格式正確。使用 ^ 元字符可指定字符串(或行)的開始,使用 $ 元字符可指定字符串(或行)的結束。通過將這些字符添加到模式的開始和結束處,可強制模式僅匹配精確匹配的輸入字符串。如果 ^ 元字符用在方括號 [ ] 指定的字符類的開頭,也有特殊的含義。具體內容見下。
/ (反斜杠)元字符既可根據特殊含義“轉義”字符,也可指定預定義集合元字符的實例。同樣,具體內容見下。為了在正則表達式中包括文字樣式的元字符,必須使用反斜杠進行“轉義”。例如,如果要匹配以“c:/”開始的字符串,可使用:^c://。注意,要使用 ^ 元字符指出字符串必須以此模式作為開始,然后用反斜杠元字符轉義文字反斜杠。
|(管道)元字符用於交替指定,特別用於在模式中指定“此或彼”。例如,a|b 將匹配包含“a”或“b”的任何輸入內容,這與字符類 [ab] 非常類似。
最后,括號 ( ) 用於給模式分組【如下圖,可通過match.groups獲取各個組的值】。它允許使用限定符讓一個完整模式出現多次。為了便於閱讀,或分開匹配特定的輸入部分,可能允許分析
或重新設置格式。
下面列出元字符的一些使用示例。
| 模式 |
輸入(匹配) |
|---|---|
| . |
a、b、c、1、2、3 |
| .* |
Abc, 123, 任意字符串, 無字符時也匹配 |
| ^c:// |
c:/windows、c://///、c:/foo.txt、c:/ 后跟任何其他內容 |
| abc$ |
abc、123abc、以 abc 結束的任意字符串 |
| (abc){2,3} |
abcabc、abcabcabc |
字符類
字符類是正則表達式中的“迷你”語言,在方括號 [ ] 中定義。最簡單的字符類只不過是括號中的一個字符表,如 [aeiou]。在表達式中使用字符類時,可在模式的此位置使用其中任何一個字符(但只能使用一個字符,除非使用了限定符)。請注意,不能使用字符類定義單詞或模式,只能定義單個字符。
要指定任何數值數字,可以使用字符類 [0123456789]。但是,由於這樣使用字符不大方便,所以要通過在括號中使用連字符 - 來定義字符的范圍。連字符在字符類中有特殊的含義(不是在正則表達式中,因此,准確地說它不能叫正則表達式元字符),且僅在連字符不是第一個字符時,連字符才在字符類中有特殊含義。要使用連字符指定任何數值數字,可以使用 [0-9]。小寫字母也一樣,可以使用 [a-z],大寫字母可以使用 [A-Z]。連字符定義的范圍取決於使用的字符集。因此,字符在(例如)ASCII 或 Unicode 表中出現的順序確定了在范圍中包括的字符。如果需要在范圍中包括連字符,將它指定為第一個字符。例如:[-.?] 將匹配 4 個字符中任何一個字符(注意,最后的字符是個空格)。另請注意,正則表達式元字符在字符類中不做特殊處理,所以這些元字符不需要轉義。考慮到字符類是與其他正則表達式語言分開的一種語言,因此字符類有自己的規則和語法。
如果使用字符 ^ 作為字符類[]的第一個字符來否定此類,也可以匹配字符類成員以外的任何字符。因此,要匹配任何非元音字符,可以使用字符類 [^aAeEiIoOuU]。注意,如果要否定連字符,應將連字符作為字符類的第二個字符,如 [^-]。記住,^ 在字符類中的作用與它在正則表達式模式中的作用完全不同。
下面列出操作中使用的一些字符類。
| 模式 |
輸入(匹配) |
|---|---|
| ^b[aeiou]t$ |
Bat、bet、bit、bot、but |
| ^[0-9]{5}$ |
11111, 12345, 99999 |
| ^c:// |
c:/windows、c://///、c:/foo.txt、c:/ 后跟任何其他內容 |
| abc$ |
abc、123abc、以 abc 結束的任意字符串 |
| (abc){2,3} |
abcabc、abcabcabc |
| ^[^-][0-9]$ |
0、1、2、... (不匹配 -0、-1、 -2 等) |
在 .NET Framework 的下一版中,代碼名“Whidbey”作為一種新功能被添加到字符類中,稱作字符類差 (character class subtraction)。它的主要作用是,允許從一個字符類中減去另一個字符類,可提供更可讀的方式描述某些模式。該規范可通過以下地址訪問:http://www.gotdotnet.com/team/clr/bcl/TechArticles/techarticles/Specs/Regex/CharacterClassSubtraction.doc。它的語法類似 [a-z-[aeiou]],匹配所有的小寫輔音字母。
預定義的集合元字符
使用目前提供的工具可以完成很多工作。但是,要使用 [0-9] 表示模式中的每個數值數字,或(更糟)使用 [0 -9a -zA-Z]表示任何字母數字字符,還有一段相當漫長的過程。為了減輕處理這些常用但冗長模式的痛苦,事先定義了預定義元字符集合。正則表達式的不同實現定義了不同的預定義元字符集合,下面描述的預定義元字符集合在 .NET Framework 中得到 System.Text.RegularExpressions API 的支持。這些預定義元字符的標准語法是,在反斜杠 / 后跟一個或多個字符。多數預定義元字符只有一個字符,它們的使用很容易,是冗長字符類的理想替代字符。以下是兩個示例:/d 匹配所有數值數字,/w 匹配所有單詞字符(字母數字加下划線)。例外情況是一些特定字符代碼匹配,此時必須指定所匹配字符的地址,如 /u000D 將匹配 Unicode 回車符。下面列出一些最常用的字符類及其等效的元字符。
| 元字符 |
等效字符類 |
|---|---|
| /a |
匹配鈴聲(警報);/u0007 |
| /b |
匹配字符類外的字邊界,它匹配退格字符,/u0008 |
| /t |
匹配制表符,/u0009 |
| /r |
匹配回車符,/u000D |
| /w |
匹配垂直制表符,/u000B |
| /f |
匹配換頁符,/u000C |
| /n |
匹配新行,/u000A |
| /e |
匹配轉義符,/u001B |
| /040 |
匹配 3 位 8 進制 ASCII 字符。/040 表示空格(十進制數 32)。 |
| /x20 |
使用 2 位 16 進制數匹配 ASCII 字符。此例中,/x2- 表示空格。 |
| /cC |
匹配 ASCII 控制字符,此例中是 ctrl-C。 |
| /u0020 |
使用 4 位 16 進制數匹配 Unicode 字符。此例中 /u0020 是空格。 |
| /* |
不代表預定義字符類的任意字符都只作為該字符本身對待。因此,/* 等同於 /x 2A(是文字 *,不是 * 元字符)。 |
| /p{name} |
匹配已命名字符類“name”中的任意字符。支持名稱是 Unicode 組和塊范圍。例如,Ll、Nd、Z、IsGreek、IsBoxDrawing 和 Sc(貨幣)。 |
| /p{name} |
匹配已命名字符類“name”中不包括的文本。 |
| /w |
匹配任意單詞字符。對於非 Unicode 和 ECMAScript 實現,這等同於 [a-zA-Z_0-9]。在 Unicode 類別中,這等同於 [/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}]。 |
| /W |
/w 的否定,等效於 ECMAScript 兼容集合 [^a-zA-Z_0-9] 或 Unicode 字符類別 [^/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}]。 |
| /s |
匹配任意空白區域字符。等效於 Unicode 字符類 [/f/n/r/t/v/x85/p{Z}]。如果使用 ECMAScript 選項指定 ECMAScript 兼容方式,/s 等效於 [ /f/n/r/t/v] (請注意前導空格)。 |
| /S |
匹配任意非空白區域字符。等效於 Unicode 字符類別 [^/f/n/r/t/v/x85/p{Z}]。如果使用 ECMAScript 選項指定 ECMAScript 兼容方式,/S 等效於 [^ /f/n/r/t/v] (請注意 ^ 后的空格)。 |
| /d |
匹配任意十進制數字。在 ECMAScript 方式下,等效於 Unicode 的 [/p{Nd}]、非 Unicode 的 [0-9]。 |
| /D |
匹配任意非十進制數字。在 ECMAScript 方式下,等效於 Unicode 的 [/p{Nd}]、非 Unicode 的 [^0-9]。 |
表達式示例
很多人都喜歡通過示例學習,下面即提供一些表達式示例。要獲取更多示例,請訪問以下地址中的正則表達式聯機數據庫:http://www.regexlib.com/。
| 模式 |
說明 |
|---|---|
| ^/d{5}$ |
5 個數值數字,如美國郵政編碼。 |
| ^(/d{5})|(/d{5})-/d{4}$ |
5 個數值數字或 5 個數字-短划線-4 個數字。匹配 5 位數字格式的美國郵政編碼,或 5 位數字 + 4 位數字格式的美國郵政編碼。 |
| ^(/d{5}(-/d{4})?$ |
與前一個相同,但更有效。使用 ? 可使模式中的 4 位數字成為可選部分,而不是要求分別比較不同的兩個模式(通過另一種方式)。 |
| ^[+-]?/d+(/./d+)?$ |
匹配任意有可選符號的實數。 |
| ^[+-]?/d*/.?/d*$ |
與上一個相同,但也匹配空字符串。 |
| ^(20|21|22|23|[01]/d)[0-5]/d$ |
匹配 24 小時制時間值。 |
| //*.*/*/ |
匹配 C 語言風格的注釋 /* ... */ |
ASP.NET 中的驗證
ASP.NET 提供了一套驗證控件,與使用舊的(或願意的話使用傳統的) ASP 處理任務相比,驗證控件使在 Web 窗體上驗證輸入變得非常容易。其中一個非常有效的驗證器是 RegularExpressionValidator,如您所料,它允許您提供必須匹配輸入的正則表達式來驗證輸入。設置控件的 ValidationExpression 屬性可指定正則表達式的模式。下面顯示了驗證郵政代碼字段的驗證程序:
<asp:RegularExpressionValidator runat="server" id="ZipCodeValidator"
ControlToValidate="ZipCodeTextBox" ErrorMessage="Invalid ZIP code
format; format should be either 12345 or 12345-6789."
ValidationExpression="(/d{5}(-/d{4})?" />
使用 RegularExpressionValidator 要注意幾個問題:
-
決不要使用驗證程序要驗證的控件中的空字符串來激活驗證器。只有 RequiredFieldValidator 才可以捕獲空字符串。
-
您無需指定匹配字符的開始與結尾(^ 和$)- 它們是事先假設的。如果添加了開始與結尾,也沒有任何影響,不需要這樣做。
-
對於所有驗證控件來說,必須在客戶端以及服務器端進行驗證。如果正則表達式不是 ECMAScript 兼容方式,客戶端驗證將失敗。為了避免這種情況,確保表達式是 ECMAScript 兼容方式,否則只在服務器端進行控件驗證。
正則表達式 API
除了 ASP.NET 驗證控件,在.NET 中使用正則表達式的大多數情況都要使用 System.Text.RegularExpressions 命名空間中發現的類。特別是那些您希望熟悉的主類 Regex、Match 和 MatchCollection。
順便說一下,正則表達式縮寫樣式 regex 的發音究竟是 /reg-eks/ 還是 /rej-eks/,還有一些爭議。本人傾向於后者,但兩種發音都有專家贊同,所以選擇哪個發音由您自己決定。
Regex 類有大量的方法和屬性,如果您以前沒有用過它,可能會感到無所適從。下面匯總了一些最常用的方法:
| 方法 |
說明 |
|---|---|
| Escape / Unescape |
字符串中的轉義元字符,用作表達式中的文字。 |
| IsMatch |
如果正則表達式在輸入字符串中發現匹配,返回“Ture”。 |
| Match |
如果在輸入字符串中發現匹配,則返回匹配對象。 |
| Matches |
如果在輸入字符串中發現包含任何或全部匹配,則返回匹配集合對象。 |
| Replace |
用給定的替換字符串替換輸入字符串中的匹配。 |
| Split |
將輸入字符串拆分成用正則表達式匹配分開的數組元素時,返回數組字符串。 |
除了指定很多方法外,還有一些選項可以指定,通常在 Regex 對象構造函數中。由於這些選項是位屏蔽的一部分,或許可以同時指定這些選項(如,可以同時指定 Multiline 和 Singleline)。
| 方法 |
說明 |
|---|---|
| Compiled |
當在循環中執行許多匹配操作時使用此選項。這可以節省每一循環的分析表達式步驟。 |
| Multiline |
它與輸入字符串中的行數沒有關系。確切地說,它只修改 ^ 和 $ 的方式,以便匹配行開始 (BOL) 和行結尾 (EOL),而不是匹配整個輸入字符串的開始和結尾。 |
| IgnoreCase |
使模式在匹配搜索字符串時忽略大小寫。 |
| IgnorePatternWhitespace |
允許根據需要在模式中包括任意數量的空白區域,也支持使用 (?# 注釋 #) 語法在模式中加入注釋。 |
| SingleLine |
它與輸入字符串中的行數沒有關系。更確切地說,它將導致 .(句點)元字符匹配任意字符,而不是除 /n 之外的任意字符(默認情況)。 |
使用正則表達式常執行的操作包括:驗證、匹配和替換。大多數情況下,可以使用 Regex 類的靜態方法完成這些操作,不需要實例化 Regex 類本身。要執行驗證,全部要做的就是必建或找到正確的表達式,然后使用 Regex 類的 IsMatch() 方法將表達式應用到輸入字符串中。例如,下面的函數演示了如何使用正則表達式驗證郵政編碼:
private void ValidateZipButton_Click(object sender, System.EventArgs e) { String ZipRegex = @"^/d{5}$"; if(Regex.IsMatch(ZipTextBox.Text, ZipRegex)) { ResultLabel.Text = "ZIP is valid!"; } else { ResultLabel.Text = "ZIP is invalid!"; } }
類似的,可以使用靜態 Replace() 方法將匹配替換為特定字符串,如下所示:
String newText = Regex.Replace(inputString, pattern, replacementText);
最后,可以使用如下代碼遍歷輸入字符串的匹配集合:
private void MatchButton_Click(object sender, System.EventArgs e) { MatchCollection matches = Regex.Matches(SearchStringTextBox.Text, MatchExpressionTextBox.Text); MatchCountLabel.Text = matches.Count.ToString(); MatchesLabel.Text = ""; foreach(Match match in matches) { MatchesLabel.Text += "Found" + match.ToString() + " at position " + match.Index + ".<br>"; } }
通常,在您需要指定默認方式以外的方式時,需要實例化 Regex 類的實例。特別是在設置選項時。例如,要創建忽略大小寫和模式空白區域的 Regex 實例,然后檢索與該表達式匹配的集合,則應使用如下代碼:
Regex re = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); MatchCollection mc = re.Matches(inputString);
下面是簡單實例,更直觀的了解groups。
1 using System; 2 using System.Text.RegularExpressions; 3 4 public class Example 5 { 6 public static void Main() 7 { 8 string pattern = @"(/d{3})-(/d{3}-/d{4})"; 9 string input = "212-555-6666 906-932-1111 415-222-3333 425-888-9999"; 10 MatchCollection matches = Regex.Matches(input, pattern); 11 12 foreach (Match match in matches) 13 { 14 Console.WriteLine("Area Code: {0}", match.Groups[1].Value); 15 Console.WriteLine("Telephone number: {0}", match.Groups[2].Value); 16 Console.WriteLine(); 17 } 18 Console.WriteLine(); 19 } 20 } 21 // The example displays the following output: 22 // Area Code: 212 23 // Telephone number: 555-6666 24 // 25 // Area Code: 906 26 // Telephone number: 932-1111 27 // 28 // Area Code: 415 29 // Telephone number: 222-3333 30 // 31 // Area Code: 425 32 // Telephone number: 888-9999
