可粗略估計一下,除了偶爾用Linux的外,其餘Linu x用戶都會遇到正則表達式。正則表達式是個極端強大工具,並且在字符串模式-匹配和字符串模式-替換方面富有彈性。在Unix世界裏,正則表達式幾乎沒有什麼限制,可確定的是,它應用很是之普遍。java
正則表達式的引擎已被許多普通的Unix工具所實現,包括grep,awk,vi和Emacs等。此外,許多使用比較普遍的腳本語言也支持正則表達式,好比Python,Tcl,JavaScript,以及最著名的Perl。正則表達式
我很早之前就是個Perl方面的***,若是你和我同樣話,你也會很是依賴你手邊的這些強大的text-munging工具。近幾年來,像其餘程序開發者同樣,我也愈來愈關注Java的開發。spring
Java做爲一種開發語言,有許多值得推薦的地方,可是它一直以來沒有自帶對正則表達式的支持。直到最近,藉助於第三方的類庫,Java開始支持正則表達式,但這些第三方的類庫都不一致、兼容性差,並且維護代碼起來很糟糕。這個缺點,對我選擇Java做爲首要的開發工具來講,一直是個巨大的顧慮之處。ide
你能夠想象,當我知道Sun的Java JDK 1.40版本包含了java.util.regex(一個徹底開放、自帶的正則表達式包)時,是多麼的高興!很搞笑的說,我花好些時間去挖掘這個被隱藏起來的寶石。我很是驚奇的是,Java這樣的一個很大改進(自帶了java.util.regex包)爲何很少公開一點呢?!工具
最近,Java雙腳都跳進了正則表達式的世界。java.util.regex包在支持正則表達也有它的過人之處,另外Java也提供詳細的相關說明文檔。使得朦朦朧朧的regex神祕景象也慢慢被撥開。有一些正則表達式的構成(可能最顯著的是,在於糅合了字符類庫)在Perl都找不到。學習
在regex包中,包括了兩個類,Pattern(模式類)和Matcher(匹配器類)。Pattern類是用來表達和陳述所要搜索模式的對象,Matcher類是真正影響搜索的對象。另加一個新的例外類,PatternSyntaxException,當遇到不合法的搜索模式時,會拋出例外。開發工具
即便對正則表達式很熟悉,你會發現,經過java使用正則表達式也至關簡單。要說明的一點是,對那些被Perl的單行匹配所寵壞的Perl狂熱愛好者來講,在使用java的regex包進行替換操做時,會比他們因此前經常使用的方法費事些。this
本文的侷限之處,它不是一篇正則表達式用法的徹底教程。若是讀者要對正則表達進一步瞭解的話,推薦閱讀Jeffrey Frieldl的Mastering Regular Expressions,該書由O’Reilly出版社出版。我下面就舉一些例子來教讀者如何使用正則表達式,以及如何更簡單地去使用它。設計
設計一個簡單的表達式來匹配任何電話號碼數字多是比較複雜的事情,緣由在於電話號碼格式有不少種狀況。全部必須選擇一個比較有效的模式。好比:(212) 555-1212, 212-555-1212和212 555 1212,某些人會認爲它們都是等價的。對象
首先讓咱們構成一個正則表達式。爲簡單起見,先構成一個正則表達式來識別下面格式的電話號碼數字:(nnn)nnn-nnnn。
第一步,建立一個pattern對象來匹配上面的子字符串。一旦程序運行後,若是須要的話,可讓這個對象通常化。匹配上面格式的正則表達能夠這樣構成:(\d{3})\s\d{3}-\d{4},其中\d單字符類型用來匹配從0到9的任何數字,另外{3}重複符號,是個簡便的記號,用來表示有3個連續的數字位,也等效於(\d\d\d)。\s也另一個比較有用的單字符類型,用來匹配空格,好比Space鍵,tab鍵和換行符。
是否是很簡單?可是,若是把這個正則表達式的模式用在java程序中,還要作兩件事。對java的解釋器來講,在反斜線字符(\)前的字符有特殊的含義。在java中,與regex有關的包,並不都能理解和識別反斜線字符(\),儘管能夠試試看。但爲避免這一點,即爲了讓反斜線字符(\)在模式對象中被徹底地傳遞,應該用雙反斜線字符(\)。此外圓括號在正則表達中兩層含義,若是想讓它解釋爲字面上意思(即圓括號),也須要在它前面用雙反斜線字符(\)。也就是像下面的同樣:
\\(\\d{3}\\)\\s\\d{3}-\\d{4}
如今介紹怎樣在java代碼中實現剛纔所講的正則表達式。要記住的事,在用正則表達式的包時,在你所定義的類前須要包含該包,也就是這樣的一行:
import java.util.regex.*;
下面的一段代碼實現的功能是,從一個文本文件逐行讀入,並逐行搜索電話號碼數字,一旦找到所匹配的,而後輸出在控制檯。
BufferedReader in;
Pattern pattern = Pattern.compile("\\(\\d{3}\\)\\s\\d{3}-\\d{4}");
in = new BufferedReader(new FileReader("phone"));
String s;
while ((s = in.readLine()) != null)
{
Matcher matcher = pattern.matcher(s);
if (matcher.find())
{
System.out.println(matcher.group());
}
}
in.close();
對那些熟悉用Python或Javascript來實現正則表達式的人來講,這段代碼很日常。在Python和Javascript這些語言中,或者其餘的語言,這些正則表達式一旦明確地編譯事後,你想用到哪裏均可以。與Perl的單步匹配相比,看起來多多作了些工做,但這並不很費事。
find()方法,就像你所想象的,用來搜索與正則表達式相匹配的任何目標字符串,group()方法,用來返回包含了所匹配文本的字符串。應注意的是,上面的代碼,僅用在每行只能含有一個匹配的電話號碼數字字符串時。能夠確定的說,java的正則表達式包能用在一行含有多個匹配目標時的搜索。本文的原意在於舉一些簡單的例子來激起讀者進一步去學習java自帶的正則表達式包,因此對此就沒有進行深刻的探討。
這至關漂亮吧! 可是很遺憾的是,這僅是個電話號碼匹配器。很明顯,還有兩點能夠改進。若是在電話號碼的開頭,即區位號和本地號碼之間可能會有空格。咱們也可匹配這些狀況,則經過在正則表達式中加入\s?來實現,其中?元字符表示在模式可能有0或1個空格符。
第二點是,在本地號碼位的前三位和後四位數字間有多是空格符,而不是連字號,更有勝者,或根本就沒有分隔符,就是7位數字連在一塊兒。對這幾種狀況,咱們能夠用(-|)?來解決。這個結構的正則表達式就是轉換器,它能匹配上面所說的幾種狀況。在()能含有管道符|時,它能匹配是否含有空格符或連字符,而尾部的?元字符表示是否根本沒有分隔符的狀況。
最後,區位號也可能沒有包含在圓括號內,對此能夠簡單地在圓括號後附上?元字符,但這不是一個很好的解決方法。由於它也包含了不配對的圓括號,好比"(555" 或 "555)"。相反,咱們能夠經過另外一種轉換器來強迫讓電話號碼是否帶有有圓括號:(\(\d{3}\)|\d{3})。若是咱們把上面代碼中的正則表達式用這些改進後的來替換的話,上面的代碼就成了一個很是有用的電話號碼數字匹配器:
Pattern pattern =
Pattern.compile("(\\(\\d{3}\\)|\\d{3})\\s?\\d{3}(-|)?\\d{4}");
能夠肯定的是,你能夠本身試着進一步改進上面的代碼。
如今看看第二個例子,它是從Friedl的中改編過來的。其功能是用來檢查文本文件中是否有重複的單詞,這在印刷排版中會常常遇到,一樣也是個語法檢查器的問題。
匹配單詞,像其餘的同樣,也能夠經過好幾種的正則表達式來完成。可能最直接的是\b\w+\b,其優勢在於只需用少許的regex元字符。其中\w元字符用來匹配從字母a到u的任何字符。+元字符表示匹配匹配一次或屢次字符,\b元字符是用來講明匹配單詞的邊界,它能夠是空格或任何一種不一樣的標點符號(包括逗號,句號等)。
如今,咱們怎樣來檢查一個給定的單詞是否被重複了三次?爲完成這個任務,需充分利用正則表達式中的所熟知的向後掃描。如前面提到的,圓括號在正則表達式中有幾種不一樣的用法,一個就是能提供組合類型,組合類型用來保存所匹配的結果或部分匹配的結果(以便後面能用到),即便遇到有相同的模式。在一樣的正則表達中,可能(也一般指望)不止有一個組合類型。在第n個組合類型中匹配結果能夠經過向後掃描來獲取到。向後掃描使得搜索重複的單詞很是簡單:\b(\w+)\s+\1\b。
圓括號造成了一個組合類型,在這個正則表示中它是第一組合類型(也是僅有的一個)。向後掃描\1,指的是任何被\w+所匹配的單詞。咱們的正則表達式所以能匹配這樣的單詞,它有一個或多個空格符,後面還跟有一個與此相同的單詞。注意的是,尾部的定位類型(\b)必不可少,它能夠防止發生錯誤。若是咱們想匹配"Paris in the the spring",而不是匹配"Java's regex package is the theme of this article"。根據java如今的格式,則上面的正則表達式就是:Pattern pattern =Pattern.compile("\\b(\\w+)\\s+\\1\\b");
最後進一步的修改是讓咱們的匹配器對大小寫敏感。好比,下面的狀況:"The the theme of this article is the Java's regex package.",這一點在regex中能很是簡單地實現,即經過使用在Pattern類中預約義的靜態標誌CASE_INSENSITIVE :
Pattern pattern =Pattern.compile("\\b(\\w+)\\s+\\1\\b",
Pattern.CASE_INSENSITIVE);
有關正則表達式的話題是很是豐富,並且複雜的,用Java來實現也很是普遍,則須要對regex包進行的完全研究,咱們在這裏所講的只是冰山一角。即便你對正則表達式比較陌生,使用regex包後會很快發現它強大功能和可伸縮性。若是你是個來自Perl或其餘語言王國的老練的正則表達式的***,使用過regex包後,你將會安心地投入到java的世界,而放棄其餘的工具,並把java的regex包當作是手邊必備的利器。