如果您曾經使用過Perl或任何其他內置正則表達式支持的語言,您壹定知道用正則表達式處理文本和匹配模式是多麽容易。如果您不熟悉這個術語,那麽“正則表達式”就是壹個字符串,它定義了搜索匹配字符串的模式。
許多語言,包括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的代表,所以我們應該在每個前面加壹個“以避免編譯錯誤”。不幸的是,轉義過程容易出錯,所以我們要小心。可以先輸入壹個沒有轉義的正則表達式,然後從左到右用“\”替換每個“\”。如果要復查,可以嘗試輸出到屏幕上。
初始化字符串後,實例化PatternCompiler對象並用PatternCompiler編譯正則表達式以創建壹個模式對象:
現在,創建壹個PatternMatcher對象並調用PatternMatcher接口的contain()方法來檢查匹配情況:
接下來,使用PatternMatcher接口返回的MatchResult對象輸出匹配的組。由於logEntry字符串包含匹配的內容,您可以看到該類的輸出如下:
3.2 HTML處理示例1
下壹個任務是分析HTML頁面中字體標簽的所有屬性。HTML頁面中的典型字體標簽如下:
該程序將以下列形式輸出每個字體標簽的屬性:
在這種情況下,我建議妳使用兩個正則表達式。第壹個,如圖11所示,從font標簽中提取" " face = "arial,serif" size = "+2" color = "red " "。
圖11:匹配字體標簽的所有屬性
第二個正則表達式如圖12所示,它將每個屬性分成名稱-值對。
圖12:匹配單個屬性並將其分成名稱-值對。
分割結果是:
現在讓我們看看完成這項任務的Java代碼。首先,創建兩個正則表達式字符串,並用perl 5編譯器將它們編譯成模式對象。編譯正則表達式時,指定perl 5編譯器。case _ insensitive _ mask選項,以便匹配操作不區分大小寫。
接下來,創建壹個執行匹配操作的Perl5Matcher對象。
假設有壹個String類型的變量html,它表示HTML文件中的壹行。如果html字符串包含字體標簽,匹配器將返回true。此時,您可以使用matcher對象返回的MatchResult對象來獲取第壹個組,該組包含FONT的所有屬性:
接下來,創建壹個PatternMatcherInput對象。這個對象允許妳從最後壹個匹配位置繼續匹配操作,所以非常適合提取字體標簽中屬性的名稱-值對。創建壹個PatternMatcherInput對象,並將要匹配的字符串作為參數傳入。然後,用匹配器實例提取每個字體的屬性。這是通過將PatternMatcherInput對象(不是string對象)指定為參數並重復調用PatternMatcher對象的contains()方法來實現的。PatternMatcherInput對象中的每次叠代都會將指針向前移動,下壹次檢測將從上壹個匹配位置之後開始。
該示例的輸出如下:
3.3 HTML處理的示例2
讓我們看看另壹個處理HTML的例子。這壹次,讓我們假設Web服務器已經從widgets.acme.com轉移到了newserver.acme.com。現在您需要修改壹些頁面中的鏈接:
執行該搜索的正則表達式如圖13所示:
圖13:修改前匹配鏈接
如果您可以匹配這個正則表達式,您可以用下面的內容替換圖13中的鏈接:
請註意,$1添加在#字符之後。Perl正則表達式語法使用$1,$2等。以表示匹配和提取的組。圖13中的表達式將所有匹配和提取的內容作為壹組附加到鏈接的後面。
現在,回到Java。正如我們之前所做的,您必須創建測試字符串,創建將正則表達式編譯成模式對象所必需的對象,並創建壹個模式匹配器對象:
接下來用com . or Inc . text . regex包的Util類的substitute()靜態方法替換,輸出結果字符串:
Util.substitute()方法的語法如下:
這個調用的前兩個參數是之前創建的PatternMatcher和Pattern對象。第三個參數是Substiution對象,它決定如何執行替換操作。這個例子使用了Perl5Substitution對象,它可以在Perl5風格中被替換。第四個參數是要替換的字符串,最後壹個參數允許您指定是否替換模式的所有匹配子字符串(Util。SUBSTITUTE_ALL)或僅指定的次數。
結論在本文中,我向您介紹了正則表達式的強大功能。只要使用正確,正則表達式可以在字符串提取和文本修改中發揮巨大作用。此外,我還通過雅加達-ORO庫介紹了如何在Java程序中使用正則表達式。由您決定是使用老式的字符串處理方法(使用StringTokenizer、charAt和substring)還是正則表達式。