好好聊一聊Java中的正則表達式

寫在前面

如果覺得有所收穫,記得點個關注和點個贊哦,非常感謝支持
正則表達式,又稱規則表達式。正則表達式通常被用來檢索、替換那些符合某個模式(規則)的文本。正則表達式是對字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個“規則字符串”,這個“規則字符串”用來表達對字符串的一種過濾邏輯。通常都是用來驗證輸入的字符串數據是否複合正則表達式定義的規範,常被用來做驗證操作。

正則表達式(regular expression),根據英文也可簡寫爲regex、regexp 或 RE。正則表達式只用於字符串,用來檢查字符串是否符合某種規定(例如只有字母、又例如沒有數字等),或者是取出符合規定的字符串子串(用於後續替換等)。

例如這裏舉個例子

#表達式
/Hello.*/g

#匹配字符串
Hello.
Hello. world
...

這裏放一個正則表達式在線測試網站,挺好用的

正則表達式規則

正則表達式的概念不難理解,若不清晰用過一兩次便懂。使用正則表達式若有困難,都是因爲語法繁多,需要記憶的內容量大。下面整理了三張正則表達式的常用語法字符表,主要來源爲菜鳥教程的正則表達式教程。分三張表,分別是:

  • 非打印字符:轉義後的字符,代表某一類字符
  • 特殊字符:含特殊含義的字符
  • 限定字符:字符次數限制

非打印字符

字符 描述 正確示例 錯誤示例
\w 數字、字母、下劃線 1/a/A/_ ./&/の/【/︹(空格)/ㄱ(換行)
\W 除數字、字母、下劃線以外 ./&/の/【/︹(空格)/ㄱ(換行) 1/a/A/_
\s 空白字符 ︹(空格)/→(製表符)/ㄱ(換行)/☇(換頁) 1/a/A/_/./&/♂
\S 除空白字符以外 1/a/A/_/./&/♂/張 ︹(空格)/→
\d 數字(單個) 1/2/3/4/5/6/7/8/9/0 a/?/&/1234(整體)
\D 除數字以外 a/B/,/%/張/︹(空格) 1/2/3/1234(整體)
\n 換行符 ㄱ(換行) 1/a/A/_/?
\t 製表符 →|(製表符) 1/a/A/_/?

特殊字符

字符 描述 正確示例 錯誤示例
. 除換行以外任意字符 1/a/A/%/_ ㄱ(換行)/123(整體)
^ 從字符串開始的位置處匹配 ^12 -> 12/123/12a ^12 -×-> a12/abc12/
$ 從字符串結束的位置處匹配(倒着) 12$ -> 12/abc12/012 12$ -×-> 123/12aaa
| [1|2|3] -> 1/2/3 [1|2|3] -×-> a/4/&
[] 方框運算符,字符集合/範圍/反向
()
{}

限定字符

字符 描述 正確示例 錯誤示例
* 匹配零個、一個或多個字符 12*3 -> 13/123/1223 12*3 -×-> 1
+ 匹配一個或多個字符 12+3 -> 123/12223 12+3 -×-> 13
? 匹配零個或一個字符 12?3 -> 13/123 12?3 -×-> 12223
{n} 字符限定n次 2{4} -> 2222 2{4} -×-> 222/22222(整體)
{n,} 字符限定最少n次 2{2,} -> 22/222/22222 2{2,} -×-> 2
{n,m} 字符限定n-m次 2{2,3} -> 22/222 2{2,3} -×-> 2/2222(整體)

除以上常用的語法字符之外,還有以下規則,需要單獨注意:

  • [ ]:方括號表示字符集合,可配合 ^ 符合表示反向的字符集合,常見用法有:
示例 說明
[12a] 可匹配 ‘1’ 或 ‘2’ 或 ‘a’
[1|2|a|,] 可匹配 ‘1’ 或 ‘2’ 或 ‘a’ 或 ‘,’
[1-9] 可匹配 ‘1’ 或 ‘2’ 或 …… 或 ‘9’
[a-z] 可匹配 ‘a’ 或 ‘b’ 或 …… 或 ‘z’
[^12a] 可匹配除了 ‘1’ 或 ‘2’ 或 ‘a’ 以外的字符
[^1-9] 可匹配除了 ‘1’ 或 ‘2’ 或 …… 或 ‘9’ 以外的字符
  • ():圓括號代表同時匹配多個字符,如匹配 hello 這五個字符,可以使用 (hello) 來匹配。但是除此之外,() 匹配到的字符串還會被緩存起來,緩存起來的子表達式可以在之後使用。例如使用([nN]o)匹配 No, get out!,不光能匹配到 No,還能通過 $1oooooooo 將原文替換成 Nooooooooo, get out!。
示例 說明
(hello) 可以匹配 “hello”,並緩存起來
(?:hello) 可以匹配 “hello”,但不緩存結果(用於匹配多字符但並不需要結果,有時能讓正則表達式更簡潔)
hello(?=AA) 可以匹配 “helloAA……” 中的 “hello”,但是不能匹配 “helloBB……” 中的 “hello”,且不緩存結果
hello(?!AA) 不能匹配 “helloAA……” 中的 “hello”,但是可以匹配 “helloBB……” 中的 “hello”,且不緩存結果
(?<=AA)hello 可以匹配 “AAhello……” 中的 “hello”,但是不能匹配 “BBhello……” 中的 “hello”,且不緩存結果
(?<!AA)hello 不能匹配 “AAhello……” 中的 “hello”,但是可以匹配 “BBhello……” 中的 “hello”,且不緩存結果

特別說明

貪婪是編程和算法中常見的概念,意思是越多越好。* 和 + 這兩個限定符都是貪婪的,如果用 hello* 或者 hello+ 來匹配 hellooooooo,它們的匹配結果都是 hellooooooo,而不是 hell、 hello 或 hellooo 或是其他的。在 * 和 + 後面加上 ? 就可以使匹配結果是非貪婪的(最小匹配)。按照上面的例子,hello*? 匹配的結果是 hell,hello+? 匹配的結果是 hello。

寫幾個看到過的正則表達式,舉幾個例子

  • 身份證校驗(簡易版)
^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
  • 不能包含數字
^[^\d]+$
  • 浮點數
^(-?\d+)(\.\d+)?$
  • 域名
^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$

Java中的正則表達式

學習過正則表達式的基本語法之後,感覺在工作中還是不夠用,還要學習 Java 中正則表達式的使用。正則表達式從 JDK 1.4 之後出現,涉及到兩個新類:Pattern 和 Matcher。Pattern 類代表一個正則表達式,而 Matcher 類代表一個正則表達式對一個字符串進行校驗後的結果,例如:

// 正則表達式,規則:不能包含數字
String regex = "^[^\\d]+$";
Pattern compile = Pattern.compile(regex);

// 匹配abcABC?&
String str = "abcABC?&";
Matcher matcher = compile1.matcher(str);

Pattern 類沒有(對外的)構造方法,因此生成一個 pattern 對象只能通過 Pattern 類的靜態方法。如果只是想檢驗字符串是否符合要求(即只需要一個boolean值),那麼使用 Pattern 類的靜態方法就可以,pattern 對象是爲了更多操作。

String regex = "^[^\\d]+$";
String str = "abcABC?&";
boolean pass = Pattern.matches(regex, str);

// ------結果:pass:true

Pattern 類多實現一些,用代碼實現正則表達式的功能,而不是完全只用字符串來實現。例如它能指定正則表達式可以同時匹配大小寫字母(指定後,正則表達式字符串即使只有 ‘a’,匹配時也可以同時匹配 ‘a’ 和 ‘A’),又或者可以用指定正則表達式分隔字符串,例如將一串帶有數字的字符串,以數字爲分隔拆分成多個子字符串。Matcher 類多實現一些對匹配結果的處理,最後提一句,Java 中使用正則表達式時,基本上要出現 \ 的地方,都要轉義成 \,例如 \d -> \d

Pattern

Pattern 類可以理解成是正則表達式,可以使用 Pattern 對象來對字符串進行正則校驗。不能通過 new 來創建 Pattern 對象,只能通過 Pattern 類的靜態方法創建。

Pattern pattern = Pattern.compile("\\w"); // 正則表達式:單個數字、字母或下劃線

日常使用 Pattern 類有兩個用途:

  • 直接校驗字符串,返回 true/false
// 校驗是否是qq郵箱
String regex = "^\\w*@qq.com$";
boolean matches = Pattern.matches(regex, "[email protected]");

這裏有一處暗坑,校驗成功要求全部匹配,如果是部分匹配,那麼返回值將是 false。

boolean matches1 = Pattern.matches("[0-9]", "123");   // false
boolean matches2 = Pattern.matches("[0-9]*", "123");  // true

  • 生成一個校驗過的結果,返回 Matcher 對象
// 正則表達式:是否是數字
String regex = "[0-9]*";
Pattern pattern = Pattern.compile(regex);

// 生成對"123"校驗的結果(結果是一個Matcher對象)
Matcher matcher = pattern.matcher("123");

Matcher 對象能做更多的事情,比如部分校驗、取出校驗結果等。Pattern 類的其他使用,例如指定正則模式 flag、分割字符串的 split() 方法(我好像覺得跟 String 對象的 split() 方法 沒有任何區別?)。

Matcher

Matcher 類可以理解成經過校驗的字符串的校驗結果,可以通過該對象處理校驗結果。Matcher 對象由 Pattern 對象的 matcher() 方法獲取:

// pattern表示正則表達式,方法內傳入待校驗的字符串
Matcher matcher = pattern.matcher("wait to check");

日常使用 Matcher 類也是兩個用途:

  • 檢查字符串中是否含正則校驗內容
// 校驗是否包含數字
Matcher matcher = Pattern.compile("[0-9]").matcher("12345");
boolean contains = matcher.find();  // true

請注意理解 find() 方法的語義,find 的意思是找到了,並不代表完全匹配,換言之只需要字符串中有正則內容就可以。因此使用 matcher.find() 和 Pattern.matches() 是不等價的。

  • 取出正則校驗結果,這裏有一個需要先指出的暗坑:在取結果之前,必須先執行 Matcher 對象的 find() 方法,如果不執行,將拋出 IllegalStateException 異常:No match found (沒找到匹配結果)。
// 正則表達式:數字(字母)數字(字母)數字(字母)
Matcher matcher = Pattern.compile("\\d+([a-z])\\d+([a-z])\\d+([a-z])").matcher("111a111b111c");
boolean find = matcher.find();      // true
String result = matcher.group();    // 111a111b111c
String result0 = matcher.group(0);  // 111a111b111c
String result1 = matcher.group(1);  // a
String result2 = matcher.group(2);  // b
String result3 = matcher.group(3);  // c

使用 group() 方法可以捕獲到正則結果,如果正則表達式中有( )括起來的內容,這部分內容可以取出來。有兩種常用的 group() 方法,一種是傳入 int 值的 group(int group) 方法,另一種是無參的 group() 方法。

  • 帶 int 參數的 group() 方法,參數爲 0 時代表整個表達式,爲 1 時代表匹配到的第 1 個( )內容,爲 2 時代表匹配到的第 2 個( )內容……以此類推,正如上面的代碼示例。如果參數是 n,但是實際上並沒有 n 個匹配內容,會拋出 IndexOutOfBoundsException 異常。

  • 無參的 group() 方法實際上就是 group(0),代表整個表達式。

此外還可以通過 groupCount() 方法獲取匹配到( )的數量。

// ...延續上面的代碼
int count = matcher.groupCount();  // 3
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章