許多語言,包括Perl、PHP、Python、JavaScript和JScript,都支持使用正則表達式來處理文本,壹些文本編輯器使用正則表達式來實現高級的“搜索-替換”功能。那麽Java呢?在撰寫本文時,壹個包含使用正則表達式進行文本處理的Java規範請求已經被認可,您可以期待在下壹版本的JDK中看到它。
但是,如果現在需要使用正則表達式呢?妳可以從Apache.org下載開源的雅加達-ORO圖書館。本文接下來的內容簡單介紹正則表達式的基礎知識,然後以雅加達-ORO API為例介紹如何使用正則表達式。
壹、正則表達式的基礎知識
先說簡單的。假設您要搜索包含字符“cat”的字符串,用於搜索的正則表達式是“cat”。如果搜索不區分大小寫,單詞“catalog”、“Catherine”和“Sophie”都可以匹配。也就是說:
1.1周期符號
假設妳在玩英文拼字遊戲,想要找到三個字母的單詞,而這些單詞必須以字母“T”開頭,以字母“N”結尾。另外,假設有壹本英語詞典,妳可以用正則表達式搜索它的所有內容。要構建這個正則表達式,可以使用通配符——句點符號“.”。這樣,完整的表達就是“t.n”,匹配“tan”、“ten”、“tin”、“ton”,也匹配“t#n”、“tpn”甚至“t n”,以及其他許多無意義的組合。這是因為句點符號匹配所有字符,包括空格、制表符甚至換行符:
1.2方括號符號
為了解決句點符號匹配範圍過寬的問題,可以在方括號(“[]”)中指定有意義的字符。此時,只有方括號中指定的字符參與匹配。即正則表達式“t[aeio]n”只匹配“tan”、“Ten”、“tin”、“ton”。但是“Toon”不匹配,因為只能匹配方括號內的單個字符:
1.3或符號
如果除了上面匹配的所有單詞之外,還想匹配“toon”,可以使用“|”運算符。運算符的基本含義是or運算。要匹配“toon”,使用正則表達式“t(a|e|i|o|oo)n”。這裏不能用方展開符號,因為方括號只允許匹配單個字符;此處必須使用括號“()”。圓括號也可以用於分組,如後面所述。
1.4表示匹配數的符號。
表1顯示了代表匹配數的符號,這些符號用於確定緊挨著符號左側的符號的出現次數:
假設我們想在壹個文本文件中搜索美國社會保險號。這個號碼的格式是999-99-9999。用於匹配它的正則表達式如圖1所示。在正則表達式中,連字符(“-”)有特殊的含義。它代表壹個範圍,例如從0到9。因此,在匹配社會安全號中的連字符時,應該在其前面添加壹個轉義字符“\”。
圖1:以123-12-1234的形式匹配所有社保號碼。
假設在搜索時,您希望連字符出現或不出現——也就是說,999-99-9999和9999999的格式是正確的。這時候可以加上“?”在連字符後面。數量限制符號,如圖2所示:
圖2:以123-12-1234和121234的形式匹配所有社保號。
讓我們看另壹個例子。美國汽車牌照的壹種格式是四個數字加兩個字母。它的正則表達式前面是數字部分“[0-9]{4}”和字母部分“[A-Z]{2}”。圖3顯示了完整的正則表達式。
圖3:匹配典型的美國汽車車牌號,比如8836KV。
1.5無符號
“”符號被稱為“否”符號。如果用在方括號中,“”表示不想匹配的字符。例如,圖4中的正則表達式匹配除了以字母“X”開頭的單詞之外的所有單詞。
圖4:匹配除了以“X”開頭的單詞之外的所有單詞
1.6括號和空白符號
假設從生日日期中提取月份部分,格式為“June 26,1951”,用來匹配這個日期的正則表達式可以如圖5所示:
圖5:匹配所有月DD、YYYY格式的日期。
新符號\s \是壹個空白符號,它匹配所有空白字符,包括制表符。如果字符串匹配正確,接下來如何提取月份部分?只需在月份周圍添加壹個括號來創建壹個組,然後使用ORO API(將在本文後面詳細討論)來提取它的值。修改後的正則表達式如圖6所示:
圖6:以月DD,YYYY格式匹配所有日期,並將月值定義為第壹組。
1.7其他符號
為簡單起見,您可以使用壹些為常見正則表達式創建的快捷符號。如表2所示:
表2:常見符號
例如,在前面的社會安全號碼示例中,只要出現“[0-9]”,我們就可以使用“\d”。修改後的正則表達式如圖7所示:
圖7:匹配123-12-1234格式的所有社保號碼。
第二,雅加達-ORO圖書館
Java程序員可以使用許多開源的正則表達式庫,其中許多支持Perl 5兼容的正則表達式語法。我在這裏選擇了雅加達-ORO正則表達式庫,這是最全面的正則表達式API之壹,它與Perl 5正則表達式完全兼容。此外,它也是優化最好的API之壹。
雅加達-ORO圖書館曾被稱為OROMatcher,Daniel Savarese慷慨地將其捐贈給了雅加達項目。您可以根據本文末尾參考資料中的說明下載它。
我將首先簡要介紹使用雅加達-ORO庫時必須創建和訪問的對象,然後介紹如何使用雅加達-ORO API。
▲pattern compler對象
首先,創建perl 5編譯器類的壹個實例,並將其分配給PatternCompiler接口對象。Perl5Compiler是PatternCompiler接口的壹個實現,它允許您將正則表達式編譯成用於匹配的模式對象。
▲圖案對象
要將正則表達式編譯成模式對象,調用編譯器對象的compile()方法,並在call參數中指定正則表達式。例如,您可以編譯正則表達式“t[aeio]n ”,如下所示:
默認情況下,編譯器創建區分大小寫的模式。所以上面的代碼編譯出來的模式只匹配“tin”、“tan”、“ten”、“ton”,不匹配“Tin”、“taN”。要創建不區分大小寫的模式,您應該在調用編譯器時指定壹個額外的參數:
創建模式對象後,可以通過PatternMatcher類將它用於模式匹配。
▲ PatternMatcher對象
PatternMatcher對象基於模式對象和字符串執行匹配檢查。您實例化壹個Perl5Matcher類,並將結果分配給PatternMatcher接口。Perl 5Matcher類是PatternMatcher接口的實現,它根據Perl5正則表達式語法執行模式匹配:
使用PatternMatcher對象,可以使用多種方法進行匹配操作。這些方法的第壹個參數是需要根據正則表達式進行匹配的字符串:
布爾匹配(字符串輸入,模式模式):當輸入字符串和正則表達式需要精確匹配時使用。換句話說,正則表達式必須完整地描述輸入字符串。
布爾匹配前綴(字符串輸入,模式模式):當正則表達式匹配輸入字符串的開頭時使用。
Boolean contains (string input,pattern pattern):當正則表達式要匹配輸入字符串的壹部分(即必須是子字符串)時使用。
此外,在上述三個方法調用中,還可以使用PatternMatcherInput對象代替String對象作為參數;此時,您可以從字符串中的最後壹個匹配位置繼續匹配。當壹個字符串可能有多個子字符串匹配給定的正則表達式時,使用PatternMatcherInput對象作為參數是很有用的。當用PatternMatcherInput對象作為參數替換String時,上述三種方法的語法如下:
布爾匹配(模式匹配輸入,模式模式)
布爾matchesPrefix(模式匹配輸入,模式模式)
布爾包含(模式匹配輸入,模式模式)
三、應用實例
讓我們看壹些雅加達-ORO圖書館的應用例子。
3.1日誌文件處理
任務:分析Web服務器日誌文件,以確定每個用戶在網站上花費的時間。在典型的BEA WebLogic日誌文件中,日誌記錄的格式如下:
分析這個日誌記錄,可以發現從這個日誌文件中可以提取出兩個東西:IP地址和頁面訪問時間。您可以使用分組符號(括號)從日誌記錄中提取IP地址和時間戳。
首先,我們來看看IP地址。IP地址由4個字節組成,每個字節的值在0到255之間,每個字節用句點分隔。因此,IP地址中的每個字節至少有壹位,最多三位。圖8顯示了為IP地址編寫的正則表達式:
圖8:匹配IP地址
IP地址中的句點字符必須轉義(前面加“\”),因為IP地址中的句點有其原始含義,而不是正則表達式語法中的特殊含義。正則表達式中句點的特殊含義在本文前面已經介紹過了。
日誌記錄的時間部分用壹對方括號括起來。可以如下提取方括號中的所有內容:首先搜索起始方括號字符("["),提取不超過結束方括號字符("]")的所有內容,向前查找,直到找到結束方括號字符。圖9顯示了這部分的正則表達式。
圖9:至少匹配壹個字符,直到找到“]”
現在,用分組符號(括號)將上面兩個正則表達式組合成壹個表達式,這樣就可以從日誌記錄中提取IP地址和時間。註意,為了匹配“-”(但不是提取),“\s-\s-\s”加在正則表達式中間。完整的正則表達式如圖10所示。
圖10:匹配IP地址和時間戳
既然已經編寫了正則表達式,就可以編寫使用正則表達式庫的Java代碼了。
要使用雅加達-ORO庫,首先創建壹個正則表達式字符串和壹個要分析的日誌字符串:
這裏使用的正則表達式幾乎與圖10中的正則表達式完全相同,只有壹個例外:在Java中,必須對每個正斜杠(\ ")進行轉義。圖10不是Java的代表,所以我們應該在每個前面加壹個“以避免編譯錯誤”。