Java正則表達式詳解仙人掌工作室 |
01-7-31 下午 04:13:03 |
如果你曾經用過Perl或任何其他內建正則表達式支持的語言,你一定知道用正則表達式處理文本和匹配模式是多么簡單。如果你不熟悉這個術語,那么“正則表達式”(Regular Expression)就是一個字符構成的串,它定義了一個用來搜索匹配字符串的模式。 |
許多語言,包括Perl、PHP、Python、JavaScript和JScript,都支持用正則表達式處理文本,一些文本編輯器用正則表達式實現高級“搜索-替換”功能。那么Java又怎樣呢?本文寫作時,一個包含了用正則表達式進行文本處理的Java規范需求(Specification Request)已經得到認可,你可以期待在JDK的下一版本中看到它。 |
然而,如果現在就需要使用正則表達式,又該怎么辦呢?你可以從Apache.org下載源代碼開放的Jakarta-ORO庫。本文接下來的內容先簡要地介紹正則表達式的入門知識,然后以Jakarta-ORO API為例介紹如何使用正則表達式。 |
一、正則表達式基礎知識 |
我們先從簡單的開始。假設你要搜索一個包含字符“cat”的字符串,搜索用的正則表達式就是“cat”。如果搜索對大小寫不敏感,單詞“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是說: |
![]() |
1.1 句點符號 |
假設你在玩英文拼字游戲,想要找出三個字母的單詞,而且這些單詞必須以“t”字母開頭,以“n”字母結束。另外,假設有一本英文字典,你可以用正則表達式搜索它的全部內容。要構造出這個正則表達式,你可以使用一個通配符——句點符號“.”。這樣,完整的表達式就是“t.n”,它匹配“tan”、“ten”、“tin”和“ton”,還匹配“t#n”、“tpn”甚至“t n”,還有其他許多無意義的組合。這是因為句點符號匹配所有字符,包括空格、Tab字符甚至換行符: |
![]() |
1.2 方括號符號 |
為了解決句點符號匹配范圍過于廣泛這一問題,你可以在方括號(“[]”)里面指定看來有意義的字符。此時,只有方括號里面指定的字符才參與匹配。也就是說,正則表達式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因為在方括號之內你只能匹配單個字符: |
![]() |
1.3 “或”符號 |
如果除了上面匹配的所有單詞之外,你還想要匹配“toon”,那么,你可以使用“|”操作符?!皘”操作符的基本意義就是“或”運算。要匹配“toon”,使用“t(a|e|i|o|oo)n”正則表達式。這里不能使用方擴號,因為方括號只允許匹配單個字符;這里必須使用圓括號“()”。圓括號還可以用來分組,具體請參見后面介紹。 |
![]() |
1.4 表示匹配次數的符號 |
表一顯示了表示匹配次數的符號,這些符號用來確定緊靠該符號左邊的符號出現的次數: |
|
假設我們要在文本文件中搜索美國的社會安全號碼。這個號碼的格式是999-99-9999。用來匹配它的正則表達式如圖一所示。在正則表達式中,連字符(“-”)有著特殊的意義,它表示一個范圍,比如從0到9。因此,匹配社會安全號碼中的連字符號時,它的前面要加上一個轉義字符“\”。 |
|
圖一:匹配所有123-12-1234形式的社會安全號碼 |
假設進行搜索的時候,你希望連字符號可以出現,也可以不出現——即,999-99-9999和999999999都屬于正確的格式。這時,你可以在連字符號后面加上“?”數量限定符號,如圖二所示: |
|
圖二:匹配所有123-12-1234和123121234形式的社會安全號碼 |
下面我們再來看另外一個例子。美國汽車牌照的一種格式是四個數字加上二個字母。它的正則表達式前面是數字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”。圖三顯示了完整的正則表達式。 |
|
圖三:匹配典型的美國汽車牌照號碼,如8836KV |
1.5 “否”符號 |
“^”符號稱為“否”符號。如果用在方括號內,“^”表示不想要匹配的字符。例如,圖四的正則表達式匹配所有單詞,但以“X”字母開頭的單詞除外。 |
|
圖四:匹配所有單詞,但“X”開頭的除外 |
1.6 圓括號和空白符號 |
假設要從格式為“June 26, 1951”的生日日期中提取出月份部分,用來匹配該日期的正則表達式可以如圖五所示: |
|
圖五:匹配所有Moth DD,YYYY格式的日期 |
新出現的“\s”符號是空白符號,匹配所有的空白字符,包括Tab字符。如果字符串正確匹配,接下來如何提取出月份部分呢?只需在月份周圍加上一個圓括號創建一個組,然后用ORO API(本文后面詳細討論)提取出它的值。修改后的正則表達式如圖六所示: |
|
圖六:匹配所有Month DD,YYYY格式的日期,定義月份值為第一個組 |
1.7 其它符號 |
為簡便起見,你可以使用一些為常見正則表達式創建的快捷符號。如表二所示: |
表二:常用符號 |
|
例如,在前面社會安全號碼的例子中,所有出現“[0-9]”的地方我們都可以使用“\d”。修改后的正則表達式如圖七所示: |
|
圖七:匹配所有123-12-1234格式的社會安全號碼 |
二、Jakarta-ORO庫 |
有許多源代碼開放的正則表達式庫可供Java程序員使用,而且它們中的許多支持Perl 5兼容的正則表達式語法。我在這里選用的是Jakarta-ORO正則表達式庫,它是最全面的正則表達式API之一,而且它與Perl 5正則表達式完全兼容。另外,它也是優化得最好的API之一。 |
Jakarta-ORO庫以前叫做OROMatcher,Daniel Savarese大方地把它贈送給了Jakarta Project。你可以按照本文最后參考資源的說明下載它。 |
我首先將簡要介紹使用Jakarta-ORO庫時你必須創建和訪問的對象,然后介紹如何使用Jakarta-ORO API。 |
▲ PatternCompiler對象 |
首先,創建一個Perl5Compiler類的實例,并把它賦值給PatternCompiler接口對象。Perl5Compiler是PatternCompiler接口的一個實現,允許你把正則表達式編譯成用來匹配的Pattern對象。 |
![]() |
▲ Pattern對象 |
要把正則表達式編譯成Pattern對象,調用compiler對象的compile()方法,并在調用參數中指定正則表達式。例如,你可以按照下面這種方式編譯正則表達式“t[aeio]n”: |
![]() |
默認情況下,編譯器創建一個大小寫敏感的模式(pattern)。因此,上面代碼編譯得到的模式只匹配“tin”、“tan”、 “ten”和“ton”,但不匹配“Tin”和“taN”。要創建一個大小寫不敏感的模式,你應該在調用編譯器的時候指定一個額外的參數: |
![]() |
創建好Pattern對象之后,你就可以通過PatternMatcher類用該Pattern對象進行模式匹配。 |
▲ PatternMatcher對象 |
PatternMatcher對象根據Pattern對象和字符串進行匹配檢查。你要實例化一個Perl5Matcher類并把結果賦值給PatternMatcher接口。Perl5Matcher類是PatternMatcher接口的一個實現,它根據Perl 5正則表達式語法進行模式匹配: |
![]() |
使用PatternMatcher對象,你可以用多個方法進行匹配操作,這些方法的第一個參數都是需要根據正則表達式進行匹配的字符串: |
· boolean matches(String input, Pattern pattern):當輸入字符串和正則表達式要精確匹配時使用。換句話說,正則表達式必須完整地描述輸入字符串。 |
· boolean matchesPrefix(String input, Pattern pattern):當正則表達式匹配輸入字符串起始部分時使用。 |
· boolean contains(String input, Pattern pattern):當正則表達式要匹配輸入字符串的一部分時使用(即,它必須是一個子串)。 |
另外,在上面三個方法調用中,你還可以用PatternMatcherInput對象作為參數替代String對象;這時,你可以從字符串中最后一次匹配的位置開始繼續進行匹配。當字符串可能有多個子串匹配給定的正則表達式時,用PatternMatcherInput對象作為參數就很有用了。用PatternMatcherInput對象作為參數替代String時,上述三個方法的語法如下: |
· boolean matches(PatternMatcherInput input, Pattern pattern) |
· boolean matchesPrefix(PatternMatcherInput input, Pattern pattern) |
· boolean contains(PatternMatcherInput input, Pattern pattern) |
三、應用實例 |
下面我們來看看Jakarta-ORO庫的一些應用實例。 |
3.1 日志文件處理 |
任務:分析一個Web服務器日志文件,確定每一個用戶花在網站上的時間。在典型的BEA WebLogic日志文件中,日志記錄的格式如下: |
![]() |
分析這個日志記錄,可以發現,要從這個日志文件提取的內容有兩項:IP地址和頁面訪問時間。你可以用分組符號(圓括號)從日志記錄提取出IP地址和時間標記。 |
首先我們來看看IP地址。IP地址有4個字節構成,每一個字節的值在0到255之間,各個字節通過一個句點分隔。因此,IP地址中的每一個字節有至少一個、最多三個數字。圖八顯示了為IP地址編寫的正則表達式: |
|
圖八:匹配IP地址 |
IP地址中的句點字符必須進行轉義處理(前面加上“\”),因為IP地址中的句點具有它本來的含義,而不是采用正則表達式語法中的特殊含義。句點在正則表達式中的特殊含義本文前面已經介紹。 |
日志記錄的時間部分由一對方括號包圍。你可以按照如下思路提取出方括號里面的所有內容:首先搜索起始方括號字符(“[”),提取出所有不超過結束方括號字符(“]”)的內容,向前尋找直至找到結束方括號字符。圖九顯示了這部分的正則表達式。 |
|
圖九:匹配至少一個字符,直至找到“]” |
現在,把上述兩個正則表達式加上分組符號(圓括號)后合并成單個表達式,這樣就可以從日志記錄提取出IP地址和時間。注意,為了匹配“- -”(但不提取它),正則表達式中間加入了“\s-\s-\s”。完整的正則表達式如圖十所示。 |
|
圖十:匹配IP地址和時間標記 |
現在正則表達式已經編寫完畢,接下來可以編寫使用正則表達式庫的Java代碼了。 |
為使用Jakarta-ORO庫,首先創建正則表達式字符串和待分析的日志記錄字符串: |
![]() |
這里使用的正則表達式與圖十的正則表達式差不多完全相同,但有一點例外:在Java中,你必須對每一個向前的斜杠(“\”)進行轉義處理。圖十不是Java的表示形式,所以我們要在每個“\”前面加上一個“\”以免出現編譯錯誤。遺憾的是,轉義處理過程很容易出現錯誤,所以應該小心謹慎。你可以首先輸入未經轉義處理的正則表達式,然后從左到右依次把每一個“\”替換成“\\”。如果要復檢,你可以試著把它輸出到屏幕上。 |
初始化字符串之后,實例化PatternCompiler對象,用PatternCompiler編譯正則表達式創建一個Pattern對象: |
![]() |
現在,創建PatternMatcher對象,調用PatternMatcher接口的contain()方法檢查匹配情況: |
![]() |
接下來,利用PatternMatcher接口返回的MatchResult對象,輸出匹配的組。由于logEntry字符串包含匹配的內容,你可以看到類如下面的輸出: |
![]() |
3.2 HTML處理實例一 |
下面一個任務是分析HTML頁面內FONT標記的所有屬性。HTML頁面內典型的FONT標記如下所示: |
![]() |
程序將按照如下形式,輸出每一個FONT標記的屬性: |
![]() |
在這種情況下,我建議你使用兩個正則表達式。第一個如圖十一所示,它從字體標記提取出“"face="Arial, Serif" size="+2" color="red"”。 |
|
圖十一:匹配FONT標記的所有屬性 |
第二個正則表達式如圖十二所示,它把各個屬性分割成名字-值對。 |
|
圖十二:匹配單個屬性,并把它分割成名字-值對 |
分割結果為: |
![]() |
現在我們來看看完成這個任務的Java代碼。首先創建兩個正則表達式字符串,用Perl5Compiler把它們編譯成Pattern對象。編譯正則表達式的時候,指定Perl5Compiler.CASE_INSENSITIVE_MASK選項,使得匹配操作不區分大小寫。 |
接下來,創建一個執行匹配操作的Perl5Matcher對象。 |
![]() |
假設有一個String類型的變量html,它代表了HTML文件中的一行內容。如果html字符串包含FONT標記,匹配器將返回true。此時,你可以用匹配器對象返回的MatchResult對象獲得第一個組,它包含了FONT的所有屬性: |
![]() |
接下來創建一個PatternMatcherInput對象。這個對象允許你從最后一次匹配的位置開始繼續進行匹配操作,因此,它很適合于提取FONT標記內屬性的名字-值對。創建PatternMatcherInput對象,以參數形式傳入待匹配的字符串。然后,用匹配器實例提取出每一個FONT的屬性。這通過指定PatternMatcherInput對象(而不是字符串對象)為參數,反復地調用PatternMatcher對象的contains()方法完成。PatternMatcherInput對象之中的每一次迭代將把它內部的指針向前移動,下一次檢測將從前一次匹配位置的后面開始。 |
本例的輸出結果如下: |
![]() |
3.3 HTML處理實例二 |
下面我們來看看另一個處理HTML的例子。這一次,我們假定Web服務器從widgets.acme.com移到了newserver.acme.com?,F在你要修改一些頁面中的鏈接: |
![]() |
執行這個搜索的正則表達式如圖十三所示: |
|
圖十三:匹配修改前的鏈接 |
如果能夠匹配這個正則表達式,你可以用下面的內容替換圖十三的鏈接: |
![]() |
注意#字符的后面加上了$1。Perl正則表達式語法用$1、$2等表示已經匹配且提取出來的組。圖十三的表達式把所有作為一個組匹配和提取出來的內容附加到鏈接的后面。 |
現在,返回Java。就象前面我們所做的那樣,你必須創建測試字符串,創建把正則表達式編譯到Pattern對象所必需的對象,以及創建一個PatternMatcher對象:![]() |
接下來,用com.oroinc.text.regex包Util類的substitute()靜態方法進行替換,輸出結果字符串: |
![]() |
Util.substitute()方法的語法如下: |
![]() |
這個調用的前兩個參數是以前創建的PatternMatcher和Pattern對象。第三個參數是一個Substiution對象,它決定了替換操作如何進行。本例使用的是Perl5Substitution對象,它能夠進行Perl5風格的替換。第四個參數是想要進行替換操作的字符串,最后一個參數允許指定是否替換模式的所有匹配子串(Util.SUBSTITUTE_ALL),或只替換指定的次數。 |
【結束語】在這篇文章中,我為你介紹了正則表達式的強大功能。只要正確運用,正則表達式能夠在字符串提取和文本修改中起到很大的作用。另外,我還介紹了如何在Java程序中通過Jakarta-ORO庫利用正則表達式。至于最終采用老式的字符串處理方式(使用StringTokenizer,charAt,和substring),還是采用正則表達式,這就有待你自己決定了。 |
![]() |

ASP.NET 中的正則表達式
速成課程
Steven A. Smith
適用范圍:
Microsoft? .NET Framework
Microsoft? ASP.NET
正則表達式 API
摘要:正則表達式是一種處理文本的有用工具。無論是驗證用戶輸入、搜索字符串內的模式、還是以各種有效方式重新設置文本格式,正則表達式都非常有用。

本頁內容
![]() |
引言 |
![]() |
正則表達式使用歷史簡介 |
![]() |
簡單表達式 |
![]() |
限定符 |
![]() |
元字符 |
![]() |
字符類 |
![]() |
預定義的集合元字符 |
![]() |
表達式示例 |
![]() |
ASP.NET 中的驗證 |
![]() |
正則表達式 API |
![]() |
免費工具 |
![]() |
高級主題 |
![]() |
結論 |
![]() |
資源 |
![]() |
作者簡介 |
引言
Microsoft?.NET Framework 對正則表達式的支持是一流的,甚至在 Microsoft? ASP.NET 中也有依賴正則表達式語言的控件。本文介紹了深入學習正則表達式的基礎知識和推薦內容。
本文主要面向對正則表達式知之甚少或沒有使用經驗,但卻熟悉 ASP.NET、可借助 .NET 編程的初學者。此外,希望本文連同 regular expression cheat sheet 成為有正則表達式使用經驗的開發者的手頭參考資料或進修資料。本文討論內容如下:
1. |
正則表達式使用歷史簡介 |
2. |
簡單表達式 |
3. |
限定符 |
4. |
元字符 |
5. |
字符類 |
6. |
預定義的集合元字符 |
7. |
表達式示例詳細內容 |
8. |
ASP.NET 中的驗證 |
9. |
正則表達式 API |
10. |
免費工具 |
11. |
高級主題概述 |
12. |
小結和其他資源 |
通常,如果對本文或對正則表達式有疑問,請訪問 http://www.aspadvice.com/,通過 regex mailing list 提出問題。編寫此文時其中已有 350 多個訂戶參與。
正則表達式使用歷史簡介
正則表達式設計于五十年代,存在至今。正則表達式最初用于描述“正則集”,它們是一些神經生理學家研究的模式。正則表達式最早由數學家 Stephen Kleene 提出,最終由 Ken Thompson 在兩種非常流行的文本實用程序 qed 和 grep 中使用。Jeffrey Friedl 在其著作“Mastering Regular Expressions (2nd edition)”中對此作了進一步闡述。建議那些希望更多了解正則表達式理論和歷史的人看看這本書。
在最近的五十年中,正則表達式逐漸從模糊深奧的數學概念發展為在各類工具和軟件包中應用的主要功能。盡管數十年來很多 UNIX 工具都支持正則表達式,但僅僅是近十年來,它才在大部分 Windows 開發者工具包中得到體現。在 Microsoft? Visual Basic? 6 或 Microsoft? VBScript 中,即使情況理想,正則表達式仍難以使用。但隨著.NET Framework 的推行,正則表達式的支持發展到極點,所有 Microsoft 開發者和所有 .NET 語言都可以使用正則表達式。
那么,正則表達式究竟是什么呢?正則表達式是一種語言,它可以明確描述文本字符串中的模式。除了簡單描述這些模式之外,正則表達式引擎通??捎糜诒闅v匹配,并使用模式作為分隔符來將字符串解析為子字符串,或以智能方式替換文本或重新設置文本格式。正則表達式為解決與文本處理有關的許多常見任務提供了有效而簡捷的方式。
在討論正則表達式時,通常以正則表達式匹配(或不匹配)的文本為基礎分析正則表達式。本文(以及 System.Text.RegularExpressions 類)將在正則表達式交互操作中引用 3 個參與對象:正則表達式的“模式”、“輸入”字符串和字符串內的所有模式的“匹配”。
簡單表達式
最簡單的正則表達式大家都已熟悉,即文字字符串。特定的字符串可通過文字本身加以描述;像 foo 這樣的正則表達式模式可精確匹配輸入的字符串 foo。在本例中,也將匹配如下輸入:The food was quite tasty,如果希望精確匹配,這可能不是預期結果。
當然,使用正則表達式匹配等于它自身的精確字符串是沒有價值的實現,不能體現正則表達式的真正作用。假如不查找 foo,而是查找以字母 f 開頭的所有單詞,或所有 3 個字母的單詞,那該怎么辦?目前,這超出了文字字符串的合理范圍。我們需要更加深入地研究正則表達式。下面是一個文字表達式示例及一些匹配的輸入。
模式 | 輸入(匹配) |
foo |
foo、food、foot、“There's evil afoot.” |
限定符
限定符提供了一種簡單方法,用于指定在模式中允許特定字符或字符集自身重復出現的次數。有 3 個非顯式限定符:
1. |
*,描述“出現 0 或多次”。 |
2. |
+,描述“出現 1 或多次”。 |
3. |
?,描述“出現 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 次之外,? 字符還可強制模式或子模式匹配數目最少的字符(如果匹配輸入字符串中的多個字符)。
除了非顯式限定符(一般叫做限定符,但為區別于下一組,故稱非顯式限定符)之外,還有顯式限定符。在模式出現次數方面,限定符的概念非常模糊。使用顯式限定符則可準確指定數字、范圍或數字集。顯式限定符位于所應用的模式的后邊,這一點與正則限定符一樣。顯式限定符使用花括號 {} 及其中的數字值表示模式出現次數的上下限。例如,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 |
元字符
在正則表達式中,有一種意義特殊的構造,即元字符。目前已知的元字符有很多,如 *、?、+ 和 {} 字符。其他字符在正則表達式語言中都有特殊的含義。這些字符包括:$ ^ . [ ( | ) ] 和 \。
.(句點或點)元字符是最簡單但最常用的一個字符。它可匹配任何單字符。如果要指定某些模式可包含任意組合的字符,使用句點非常有用,但一定要在特定長度范圍內。此外,我們知道表達式將對包含在較長字符串中的所有模式進行匹配,假如只需要精確匹配模式,又該怎么辦?這在驗證方案中經常出現,例如,要確保用戶輸入的郵政編碼或電話號碼的格式正確。使用 ^ 元字符可指定字符串(或行)的開始,使用 $ 元字符可指定字符串(或行)的結束。通過將這些字符添加到模式的開始和結束處,可強制模式僅匹配精確匹配的輸入字符串。如果 ^ 元字符用在方括號 [ ] 指定的字符類的開頭,也有特殊的含義。具體內容見下。
\ (反斜杠)元字符既可根據特殊含義“轉義”字符,也可指定預定義集合元字符的實例。同樣,具體內容見下。為了在正則表達式中包括文字樣式的元字符,必須使用反斜杠進行“轉義”。例如,如果要匹配以“c:\”開始的字符串,可使用:^c:\\。注意,要使用 ^ 元字符指出字符串必須以此模式作為開始,然后用反斜杠元字符轉義文字反斜杠。
|(管道)元字符用于交替指定,特別用于在模式中指定“此或彼”。例如,a|b 將匹配包含“a”或“b”的任何輸入內容,這與字符類 [ab] 非常類似。
最后,括號 ( ) 用于給模式分組。它允許使用限定符讓一個完整模式出現多次。為了便于閱讀,或分開匹配特定的輸入部分,可能允許分析或重新設置格式。
下面列出元字符的一些使用示例。
模式 | 輸入(匹配) |
. |
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 是空格。 |
\* |
不代表預定義字符類的任意字符都只作為該字符本身對待。因此,\* 等同于 \x2A(是文字 *,不是 * 元字符)。 |
\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);
本文的下載文件中包括這些示例的完整使用版本,與簡單 ASP.NET 頁中的一樣。
免費工具
Regulator (http://royo.is-a-geek.com/iserializable/regulator/) - 一種在客戶端運行的正則表達式測試工具,通過 Web 服務與 RegexLib 緊密集成,提供對“匹配”、“拆分”和“替換”等的支持。包括性能分析和語法突出顯示功能。
RegexDesigner.NET (http://www.sellsbrothers.com/tools/) - 一種功能強大的可視工具,可幫助構造并測試正則表達式。它可生成 C# 和/或 VB.NET 代碼和已編譯匯編代碼,幫助您將表達式集成到應用程序中。
Regular Expression Workbench (v2.0) (http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=C712F2DF-B026-4D58-8961-4EE2729D7322) - Eric Gunnerson 開發的工具,用于創建、測試和研究正則表達式。具有“Examine-o-matic”功能,允許將鼠標懸停在正則表達式的上方,對其含義進行解碼。
高級主題
正則表達式有兩個不得不說的功能,一個是“命名組”,另一個是“四向處理”(lookaround processing)。由于這些功能很少使用,此處只簡單闡述一下。
使用命名組,您可單獨命名匹配組,然后在表達式中使用程序語言引用這些組。如果結合 Replace 方法重新設置輸入字符串的格式(通過重新排列順序、替換輸入字符串中的元素),這個功能特別有效。例如,假設日期使用 MM/DD/YYYY 格式的字符串,而您希望日期格式是 DD-MM-YYYY。此時,可編寫一個表達式捕獲第一種格式,遍歷它的匹配集合,并分析每個字符串,然后使用字符串操作建立替換字符串。這需要大量的代碼和大量的處理。如果使用命名組,您可完成同樣的任務,具體見下:
String MDYToDMY(String input) { return Regex.Replace(intput, @"\b(?<month>\d{1,2})/(?<day>\d{1,2}/(?<year>\d{4})\b", "${day}- ${month}-${year}"); }
您還可以按編號或按名稱引用組。在任何情況下,這種引用通稱作“反向引用”。另一個經常使用反向引用的場合在匹配表達式本身,如下這個表達式用于查找重復的字母:[a-z]\1。它將匹配“aa”、“bb”、“cc”,但它不同于 [a-z]{2} 或 [a-z][a-z],后兩者是等效的,后兩者允許匹配“ab”或“ac”或任何其他兩個字母的組合。反向引用允許表達式記住表達式已經分析并匹配過的輸入字符串中的部分字符。
“四向處理”指很多正則表達式引擎所支持的正負 Lookahead 和 Lookbehind 功能。并不是所有的正則表達式引擎都支持驗證四向處理。這些構造不使用字符,即使它們可以匹配字符。有些模式可能在不使用四向處理的情況下無法描述。特別是當模式中存在的一部分依賴于另一部分,如果不使用四向處理,則不能描述這樣的模式。下面介紹了每個四向處理的語法。
語法 | 說明 |
(?=...) |
正 Lookahead |
(?!...) |
負 Lookahead |
(?<=...) |
正 Lookbehind |
(?<!...) |
負 Lookbehind |
密碼驗證是必需四向處理的一個示例。假定在密碼限制中,密碼必須介于 4 到 8 個字符,且必須至少包含一個數字。為此,您可以僅在匹配中測試 \d,然后使用字符串操作來測試長度。但要在正則表達式中實現這一切,必須使用 Lookahead。特別是正 lookahead,如下所示:^(?=.*\d).{4,8}$
結論
正則表達式是一種描述文本模式的極有效方法,它使文本模式成為字符串驗證和操作的極好資源。.NET Framework 通過 System.Text.RegularExpressions 命名空間(特別是其中的 Regex 類)提供了對正則表達式的強大支持。使用 API 很簡單,但編寫正確的正則表達式通常比較費力。但幸運的是,正則表達式可以重復使用,您可以通過網絡中的各種聯機資源,從中找出其他人設計的表達式,或者在創建表達式遇到困難時得到幫助。
資源
正則表達式庫 http://www.regexlib.com/
正則表達式討論列表 http://aspadvice.com/login.aspx?ReturnUrl=%2fSignUp%2flist.aspx%3fl%3d68%26c%3d16&l=68&c=16
正則表達式論壇 http://forums.regexadvice.com/
正則表達式 Web 日志 http://blogs.regexadvice.com/
Mastering Regular Expressions (O'Reilly),作者 Jeffrey Friedl http://www.regex.info/
.NET 正則表達式參考 http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemTextRegularExpressions.asp
Jscript 正則表達式語法 http://www.msdn.microsoft.com/library/en-us/script56/html/js56jsgrpRegExpSyntax.asp
正則表達式信息 http://www.regular-expressions.info/
作者簡介
Steven A. Smith 是 Microsoft 在 ASP.NET 技術方面的最有價值專家 (MVP),是 ASPAlliance.com 和 DevAdvice.com 的總裁和所有者。此外,他也是 ASPSmith Ltd(一家重點提供 .NET 培訓的公司)的所有者和首席教師。他撰寫了兩本著作:“ASP.NET Developer's Cookbook”和“ASP.NET By Example”,并在 MSDN 和 AspNetPRO 雜志上發表了一些文章。Steve 每年都在各種會議上發表演講,是 INETA 聯絡處的成員。Steve 擁有企業管理碩士學位及計算機科學工程理學士學位。
如果要聯系 Steve,請發送電子郵件至 ssmith@aspalliance.com。
? 2004 Microsoft Corporation 版權所有。保留所有權利。使用規定。
|