1. 程式人生 > 實用技巧 >Java-正則表示式

Java-正則表示式

   正則表示式(Regular Expression)又稱正規表示法,常規表示法,在程式碼中常簡寫為regex,regexp或RE,他是電腦科學的一個概念。

   正則表示式是一個強大的字串處理工具,可以對字串進行纏著,提取,分割,替換等操作,是一種可以給用於模式匹配和替換的規範。一個正則表示式就是由普通的字元(如字元a~z)以及特殊字元(元字元)組成的文字模式,它用以描述在查詢文字主題是待匹配的一個或多個字串。

   String類裡也提供瞭如下幾個特殊的方法。

  • boolean matches(String regex):判斷該字串是否匹配指定的正則表示式。
  • String replaceAll(String regex,String replacement):將蓋子複查UN中所有匹配regex的子串替換成replacement。
  • String replaceFirst(String regex,String replacement):將該字串中第一個匹配regex的子串替換成replacement。
  • String[] split(String regex):以regex作為分割符,把該字串分割成多個子串。

   上面這些特殊的方法都依賴於Java提供的正則表示式,除此之外,Java還提供了Pattern和Matcher連個類轉夢用於提供正則表示式支援。

   很多讀者都會覺得正則表示式是一個非常神奇,高階的知識,其實正則表示式是一個非常簡單而非常使用的工具。正則表示式是一個用於匹配字串的模板。實際上,任意字串都可以當成正則表示式使用。例如“abc”,它也是一個正則表示式,知識它能匹配“abc”字串。

   如果正則表示式僅能匹配“abc”這樣的字串,那麼正則表示式也就不值得學習了。正則表示式作為一個用於匹配字串的模板,將某個字元模式於所搜尋的字串進行匹配。本文簡單瞭解一下如何使用正則表示式來操作字串。

正則表示式支援字元

穿件正則表示式就是建立一個特殊的字串。正則表示式所有支援的合法字元如表1所示。

  

表 1 正則表示式所支援的合法字元
字元解釋
X 字元x(x 可代表任何合法的字元)
\0mnn 八進位制數 0mnn 所表示的字元
\xhh 十六進位制值 0xhh 所表示的字元
\uhhhh 十六進位制值 0xhhhh 所表示的 Unicode 字元
\t 製表符(“\u0009”)
\n 新行(換行)符(‘\u000A’)
\r 回車符(‘\u000D’)
\f 換頁符(‘\u000C’)
\a 報警(bell)符(‘\u0007’)
\e Escape 符(‘\u001B’)
\cx x 對應的的控制符。例如,\cM匹配 Ctrl-M。x 值必須為 A~Z 或 a~z 之一。


除此之外,正則表示式中有一些特殊字元,這些特殊字元在正則表示式中有其特殊的用途,比如前面介紹的反斜線\

如果需要匹配這些特殊字元,就必須首先將這些字元轉義,也就是在前面新增一個反斜線\。正則表示式中的特殊字元如表2所示。

表 2 正則表示式中的特殊字元
特殊字元說明
$ 匹配一行的結尾。要匹配 $ 字元本身,請使用\$
^ 匹配一行的開頭。要匹配 ^ 字元本身,請使用\^
() 標記子表示式的開始和結束位置。要匹配這些字元,請使用\(\)
[] 用於確定中括號表示式的開始和結束位置。要匹配這些字元,請使用\[\]
{} 用於標記前面子表示式的出現頻度。要匹配這些字元,請使用\{\}
* 指定前面子表示式可以出現零次或多次。要匹配 * 字元本身,請使用\*
+ 指定前面子表示式可以出現一次或多次。要匹配 + 字元本身,請使用\+
? 指定前面子表示式可以出現零次或一次。要匹配 ?字元本身,請使用\?
. 匹配除換行符\n之外的任何單字元。要匹配.字元本身,請使用\.
\ 用於轉義下一個字元,或指定八進位制、十六進位制字元。如果需匹配\字元,請用\\
| 指定兩項之間任選一項。如果要匹配字元本身,請使用\|


將上面多個字元拼起來,就可以建立一個正則表示式。例如:

"\u0041\\\\" // 匹配 A\
"\u0061\t" // 匹配a<製表符>
"\\?\\[" // 匹配?[

注意:可能大家會覺得第一個正則表示式中怎麼有那麼多反斜槓?這是由於 Java 字串中反斜槓本身需要轉義,因此兩個反斜槓(\\)實際上相當於一個(前一個用於轉義)。

上面的正則表示式依然只能匹配單個字元,這是因為還未在正則表示式中使用“萬用字元”,“萬用字元”是可以匹配多個字元的特殊字元。正則表示式中的“萬用字元”遠遠超出了普通萬用字元的功能,它被稱為預定義字元,正則表示式支援如表 3 所示的預定義字元。

表 3 預定義字元
預定義字元說明
. 可以匹配任何字元
\d 匹配 0~9 的所有數字
\D 匹配非數字
\s 匹配所有的空白字元,包括空格、製表符、回車符、換頁符、換行符等
\S 匹配所有的非空白字元
\w 匹配所有的單詞字元,包括 0~9 所有數字、26 個英文字母和下畫線_
\W 匹配所有的非單詞字元


上面的 7 個預定義字元其實很容易記憶,其中:

  • d 是 digit 的意思,代表數字。
  • s 是 space 的意思,代表空白。
  • w 是 word 的意思,代表單詞。
  • d、s、w 的大寫形式恰好匹配與之相反的字元。


有了上面的預定義字元後,接下來就可以建立更強大的正則表示式了。例如:

c\\wt // 可以匹配cat、cbt、cct、cOt、c9t等一批字串
\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d // 匹配如 000-000-0000 形式的電話號碼

在一些特殊情況下,例如,若只想匹配 a~f 的字母,或者匹配除 ab 之外的所有小寫字母,或者匹配中文字元,上面這些預定義字元就無能為力了,此時就需要使用方括號表示式,方括號表示式有如表 4 所示的幾種形式。

表 4 方括號表示式
方括號表示式說明
表示列舉 例如[abc]表示 a、b、c 其中任意一個字元;[gz]表示 g、z 其中任意一個字元
表示範圍:- 例如[a-f]表示 a~f 範圍內的任意字元;[\\u0041-\\u0056]表示十六進位制字元 \u0041 到 \u0056 範圍的字元。範圍可以和列舉結合使用,如[a-cx-z],表示 a~c、x~z 範圍內的任意字元
表示求否:^ 例如[^abc]表示非 a、b、c 的任意字元;[^a-f]表示不是 a~f 範圍內的任意字元
表示“與”運算:&& 例如[a-z&&[def]]是 a~z 和 [def] 的交集,表示 d、e
f[a-z&&^bc]]是 a~z 範圍內的所有字元,除 b 和 c 之外
[ad-z] [a-z&&[m-p]]是 a~z 範圍內的所有字元,除 m~p 範圍之外的字元
表示“並”運算 並運算與前面的列舉類似。例如[a-d[m-p]]表示 [a-dm-p]

方括號表示式比前面的預定義字元靈活多了,幾乎可以匹配任何字元。例如,若需要匹配所有的中文字元,就可以利用 [\\u0041-\\u0056] 形式——因為所有中文字元的 Unicode 值是連續的,只要找出所有中文字元中最小、最大的 Unicode 值,就可以利用上面形式來匹配所有的中文字元。

正則表示式還支援圓括號,用於將多個表示式組成一個子表示式,圓括號中可以使用或運算子|。例如,正則表示式“((public)|(protected)|(private))”用於匹配 Java 的三個訪問控制符其中之一。

除此之外,Java 正則表示式還支援如表 5 所示的幾個邊界匹配符。

表 5 邊界匹配符
邊界匹配符說明
^ 行的開頭
$ 行的結尾
\b 單詞的邊界
\B 非單詞的邊界
\A 輸入的開頭
\G 前一個匹配的結尾
\Z 輸入的結尾,僅用於最後的結束符
\z 輸入的結尾


前面例子中需要建立一個匹配 000-000-0000 形式的電話號碼時,使用了 \\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d 正則表示式,這看起來比較煩瑣。實際上,正則表示式還提供了數量識別符號,正則表示式支援的數量識別符號有如下幾種模式。

  • Greedy(貪婪模式):數量表示符預設採用貪婪模式,除非另有表示。貪婪模式的表示式會一直匹配下去,直到無法匹配為止。如果你發現表示式匹配的結果與預期的不符,很有可能是因為你以為表示式只會匹配前面幾個字元,而實際上它是貪婪模式,所以會一直匹配下去。
  • Reluctant(勉強模式):用問號字尾(?)表示,它只會匹配最少的字元。也稱為最小匹配模式。
  • Possessive(佔有模式):用加號字尾(+)表示,目前只有 Java 支援佔有模式,通常比較少用。


三種模式的數量表示符如表 6 所示。

表 6 三種模式的數量表示符
貪婪模式勉強模式佔用模式說明
X? X?? X?+ X表示式出現零次或一次
X* X*? X*+ X表示式出現零次或多次
X+ X+? X++ X表示式出現一次或多次
X{n} X{n}? X{n}+ X表示式出現 n 次
X{n,} X{n,}? X{n,}+ X表示式最少出現 n 次
X{n,m} X{n,m}? X{n,m}+ X表示式最少出現 n 次,最多出現 m 次


關於貪婪模式和勉強模式的對比,看如下程式碼:

String str = "hello,java!";
// 貪婪模式的正則表示式
System.out.println(str.replaceFirst("\\w*" , "■")); //輸出■,java!
// 勉強模式的正則表示式
System.out.println(str.replaceFirst("\\w*?" , "■"")); //輸出■hello, java!

當從“hello java!”字串中查詢匹配\\w*子串時,因為\w*使用了貪婪模式,數量表示符*會一直匹配下去,所以該字串前面的所有單詞字元都被它匹配到,直到遇到空格,所以替換後的效果是“■,Java!”;如果使用勉強模式,數量表示符*會盡量匹配最少字元,即匹配 0 個字元,所以替換後的結果是“■hello,java!”。