Regex(regular expression,即Regex)是說明大塊文本的一種模式(pattern)。Regex引擎把這種模式運用到源檔案文本中去。最初的源文本是不相關的——它可以是文字檔、網頁的HTML原始碼、甚至是資料庫表格中的一個欄。
只需要使用幾種記號(token),你就可以描述出複雜的模式,而且你還可以對這些模式作些很酷的事,如算術運算(也就是說,計算該模式出現的次數)。
Regex應用程式
用到Regex的應用程式太多了,以至於不能在本文全部列出,但我們還是列出了一部分讓你大概看看Regex有些什麼作用:
- 驗證使用者資料的有效性,使用者資料(如電話號碼、信用卡號碼、電子郵件地址等等)可以來自網站或者本地應用程式。
- 在文字檔中搜尋諸如“莎士比亞全集”(字數不得少於10個字元或者任意指定的字元數)這樣的字句,或者搜尋“love”以及“horse”(忽略大小寫)並報告它們出現的位置和次數。
- 把重要的資料,如各個網站給出的足球比賽議程表,儲存到我自己的資料庫中去。
- 對網站搜尋引擎來說,為Regex保持一個智能的前端並寫出模式;然後所有其它工作交由.NET處理。
模式識別
模式是一個字元序列的代數運算式,它滿足遞加性(也就是說模式可以包含一系列其它模式)和遞迴性(也就是說模式可以包含子模式)。表A列出了Regex中常見的標記。
表A
標記 |
含義 |
. |
匹配任何單個字元(也就是說,“w.e”匹配“Brawley”,這裡“.”匹配單字元“l”)。 |
/b |
用來指明不用考慮前後字元的單詞(也就是說,定界符是空格、定位字元或者逗號都不要緊)。 |
/w |
匹配任何單詞字元(即A~Z、a~z,以及0~9)。 |
/W |
匹配任何非單詞字元(即除A~Z、a~z,以及0~9之外的字元。 |
/d |
匹配任何數字。 |
/D |
匹配任何非數字。 |
/. |
表示你實際上搜尋點號的換碼序列(因為點號有其它意義,所以需要用換碼的方式表示它)。 |
/s |
表示任何白空格(可以是定位字元也可以是空格,我們並不關心它具體是什麼)。 |
/S |
表示任何非白空格。 |
^ |
字串或者行的起始標記。 |
$ |
字串或者行的結束標記。 |
* |
用於指示零或者大於零發生了。 |
? |
指示任一字元(也就是說,零或者一發生了。 |
+ |
前一標記出現一次或者多次(也就是說“/w+”表示匹配任何字)。 |
[] |
表示一個範圍,如[A-Z]表明匹配任何大寫字母。 |
| |
OR(或)運算子,用來指示感興趣的匹配的集合 (也就是說,“ABC|BCD|DEF”匹配任何含有這三個序列中任意一個或者多個的字串。 |
() |
與“|”相同。 |
常見Regex標記
|
Regex選項
你可以利用各種選項來更改Regex的行為。有一個選項用來指定對應Regex的工作方式——單行還是多行。其預設值為單行工作方式,一般你在處理文字檔時需要這種方式。多行模式可以讓你把文本中的所有行當作單一對象來讀取。從資料庫中匯出文字檔(在特定行中用逗號、定位字元或者引號來表明欄位)的場合下用多行模式是適宜的。表B列出了.NET中常常用到的若干選項。 表B
選項 |
描述 |
None |
表明沒有設定選項。 |
IgnoreCase |
表明匹配時大小寫不敏感。 |
Multiline |
指定多行方式 改變^和$的含義,這樣它們匹配任何行的開頭和結尾,而不是整個字串的開頭和結尾。 |
ExplicitCapture |
指定有效捕獲是明確命名的或者按(?<name>…)這種方式編號的組 這允許圓括弧可以充當非捕獲(noncapturing)組的作用而無需使用笨拙的文法(?:…)。 |
Compiled |
指明該Regex將會編譯到彙編(assembly)中去 為Regex產生微軟中繼語言(Microsoft intermediate language ,MSIL)代碼;使得執行速度更快(但是付出了開始時間的代價)。 |
Singleline |
指定單行方式 改變句號字元(.)的意義,這樣它可以匹配任何單字元(而不是除/n之外的所有字元)。 |
IgnorePatternWhitespace |
指定該模式去處了所有的保有(unescaped)白空格,並且使得注釋以數字元(#)開頭。 (參見Character Escapes 列出的保有白空白字元。)注意,字元類從來沒有去處過白空格。 |
RightToLeft |
指明搜尋是自右向左,而不是自左向右的 帶有這個選項的Regex移向起點的左側而不是右側(因此,起點應該指定為字串的末尾而不是開頭)。為了防止Regex陷入無限迴圈之中,這個選項不能在中間(midstream)指定。不過,lookbehind構造(?<) 可以防止類似的事情發生,它可以在子模式中使用。 |
ECMAScript |
指定該Regex使能ECMAScript相容模式行為 這個選項只能與IgnoreCase和Multiline 標誌聯合使用。把 ECMAScript與任何其它標誌使用都會導致例外的發生。 |
Regex中的常用選項 |
例如,模式“M[aeiouy]”表示以字元“M”開頭,後面跟著任何母音字母(a、e、i、o、u或者y)的字元組合,而模式“M[^aeiouy]”表示字元“M”後沒有跟著母音字母的字元組合。
集合和字元集
集合和字元集用諸如[A-Za-z]這樣的文法表示。這個模式使用英語國家的所有字母字元。你可以認為集合為類似字元的總和。例如,你可以這麼寫:
[A-Za-z]
該模式比對所有的大寫和小寫字母字元,而丟棄數字、不可見(non-printable)字元等等。
Or Else
“|”和“()”可以讓你構造強大而又簡潔的模式,它們與If/ElseIf/Else構造的功能相同,但是少用了不少字元。
組
你可以定義組並給它們命名或者編號。當你鞸QL Server、Access或者Excel之類應用程式所建立的文字檔時,這個功能就顯得非常有用。假設你的源檔案包含Title、GivenName、Surname和EmailAddress資料欄(用逗號隔開)。GivenName和Surname欄會包含多個單詞,如“Don Diego”、“de la Vega”等等。
.NET中的Regex
為了在.NET中使用Regex,在你的原始碼中添加下面一行代碼:
Imports System.Text.RegularExpressions
然後你就可以層次使用Regex。考慮到Regex的強大作用,該工作的工作量幾乎為零。典型的代碼片斷最多也就是幾行。
建立測試載入器
接下來,我將建立一個簡單的網頁,它可以讓你在大塊文本上來測試任何Regex。我用的是VB.NET,但是只要對這些代碼做上一兩點的修改,也可以使用C#。
建立一個新的名為WebRegex的網路應用程式。在這個頁面上放置三個文字框。第一個文字框命名為txtPattern、第二個命名為txtSource、第三個為txtResults。為每個文字框添加一個標籤。在頁面上再添加一個按鈕控制項,命名為btnDoIt,並把它上面的文字改為“Do it”。調整後兩個文字框到合適的大小,並把它們的TextMode屬性改為Multiline。把txtResults文字框上的標籤的名字改為lblMatchCount。最後,添加一個檢驗欄控制項,把它的標籤改為MultiLine方式,並把它命名為chkMultiLine。現在,你的頁面應該如圖A所示。
Figure A
你只需要花上幾分鐘時間就可以建立好一個Regex測試載入器。雙擊按鈕控制項進入代碼視窗,添加下面的代碼:
Dim rx As Regex
Try
If chkMultiLine().Checked Then
rx = New Regex(txtPattern().Text, RegexOptions.Multiline)
Else
rx = New Regex(txtPattern().Text)
End If
Catch ex As Exception
Response.Write(ex.Message)
Exit Sub
End Try
Dim mc As MatchCollection = rx.Matches(txtSource().Text)
lblMatchCount.Text = "Found " & mc.Count.ToString & " matches."
Dim m As Match
For Each m In mc
txtResults().Text += m.Value '& " found at " & m.Index & Chr(10) & Chr(13)
Next
為了免去麻煩,我就不再寫入讀取文字檔的代碼(由於你需要做的全部事情就是在任何一個編輯器中開啟文字檔並把它的內容拷貝粘貼到txtSource文字框控制項中去)。你可能想用你剛剛開啟的HTML檔案來做這個測試。假設你想找出這個HTML檔案中所有的HTML標記。下面的模式將會為你完成這項工作:
<[^>]*>
再舉個例子,一個真正的技巧——用來匹配有效VISA卡號碼的模式:
^(?:(?:[4])(?://d{12}|//d{15}))$
下面是一個用來匹配年/月/日(年月日均是兩位元)格式的模式:
^((0?[1-9])|(1[0-2]))/((0?[1-9])|([12][0-9])|(3[01]))/((19|20)/d/d)
替換文本
即使尋找文本的功能在所有的Regex中都可以得以實現,它還是非常非常的強有力,而且它還可以做智能替換。從本質上來說,它包括了用某個模式捕獲感興趣的文本,然後調用另一個模式的“Replace”方法。例如,假設你想刪除指定HTML檔案中的所有HTML標記,你的代碼如下:
Dim rx as Regex
Dim strPattern as String = "<[^>]*>"
Dim strIn as String 'importing the text is not shown
Dim strOut as String
rx = New Regex( strPattern )
strOut = rx.Replace( strIn, "" )
上面的代碼找到所有的HTML標記並用空白來替換它們。注意,由於該檔案有可能包含原始碼,所以它不會找到單獨出現的< or >。
這種尋找/替換模式可能會花上你一段時間去領會。幸好,Visual Studio .NET提供了一個模式嚮導,該嚮導包括了一些常用模式,所以你的部分工作就縮減到做幾次點擊以及在下拉式清單中做出若干次選擇,僅此而已。
如果你剛剛接觸Regex,我建議你在熟練使用尋找模式之前,暫緩使用替換模式。
一個好工具
在本文中,我只是嘗試引起你對構造Regex的興趣並去除你對它的神秘感。我見識過Regex的威力,你不妨也試試。如果你現在正在辛辛苦苦的滿足客戶提出的要求,那麼機會可能就來了。用Regex花上較少的時間來構建強有力的解決方案吧。