java 正則表達式

java 正則表達式

正則表達式是一個非常強大的字符串處理工具,通過一種特殊的語法來描述一種模式,再通過模式可以完成字符串的匹配,萃取,替換等操作

簡例

要判斷一個字符串是否是一個郵箱,可能需要很多的判斷邏輯,使用則表達式,只需要下面代碼即可

Pattern pattern = Pattern.compile("^([a-z0-9._%+-]+)@([a-z0-9.-]+)\\.[a-z]{2,4}$");
Matcher matcher = pattern.matcher("[email protected]");
assertTrue(matcher.matches());

主要有 Pattern 對象和 Matcher 對象

  • Pattern: 模式對象
  • Matcher: 匹配的結果

部分匹配與完全匹配

Matcher 提供 matches 方法用於完全匹配,find 方法用於部分匹配,還提供 asPredicate 方法返回一個部分匹配的謂詞

Pattern 提供一個靜態方法 matches 用於完全匹配,這個方法會臨時構造 Pattern 對象,如果 Pattern 會被多次重複使用,儘量別直接使用這種方法

assertTrue(Pattern.compile("hello").matcher("hello").matches());         // 完全匹配
assertTrue(Pattern.compile("hello").matcher("hello world").find());      // 部分匹配
assertTrue(Pattern.compile("hello").asPredicate().test("hello world"));  // 部分匹配
assertTrue(Pattern.matches("hello", "hello"));                           // 完全匹配

通配符

符號 含義
c 匹配單個字符 c
. 匹配所有單個字符,換行符 \n 除外
^ 匹配字符串開始
$ 匹配字符串結束
\b 匹配字符界,字符和空白之間
\B 匹配非字符界,字符和空白之間
| 匹配前面表達式或者後面表達式
[charset] 匹配任意括號內的字符,可用 - 表示範圍,[a-z] 表示所有小寫字母
[^charset] 匹配任意括號外的字符
\d 匹配數字,相當於 [0-9]
\D 匹配非數字,相當於 [^0-9]
\s 匹配空白符,相當於 [ \f\n\r\t\v]
\S 匹配非空白符,相當於 [^ \f\n\r\t\v]
\f 匹配換頁符
\n 匹配換行符
\r 匹配回車
\t 匹配字表符
\v 匹配垂直製表符
\w 匹配字符類字符,包括下劃線,相當於 [A-Za-z0-9_]
\W 匹配非字符類字符,[^A-Za-z0-9_]
\u 匹配四位十六進制數表示的 Unicode 字符,\u00A9 例如匹配 ©
\x 匹配兩位十六進制數表示的 ascii 碼
限定符 含義
* 匹配前面字符或表達式 0 次或多次
+ 匹配前面字符或表達式 1 次或多次
? 匹配前面字符或表達式 0 次或1次,跟着其他限定詞後表示非貪婪匹配
{n} 匹配前面字符或表達式 n 次
{n,} 匹配前面字符或表達式至少 n 次
{n,m} 匹配前面字符或表達式至少 n 次,至多 m 次
assertTrue(Pattern.matches("[0-9]*", ""));
assertTrue(Pattern.matches("[0-9]?", ""));
assertTrue(Pattern.matches("[0-9]?", "1"));
assertTrue(Pattern.matches("[0-9]+", "123"));
assertTrue(Pattern.matches("[0-9]{3}", "123"));
assertTrue(Pattern.matches("[0-9]{3,4}", "123"));
assertTrue(Pattern.matches("[0-9]{3,4}", "1234"));
assertTrue(Pattern.matches("[0-9]{3,}", "1234"));
assertTrue(Pattern.matches("\\d+", "123"));
assertTrue(Pattern.matches("\\s+", " \t"));
assertTrue(Pattern.matches("\\w+", "abc"));
assertTrue(Pattern.matches("(f|z)oo", "foo"));
assertTrue(Pattern.matches("(f|z)oo", "zoo"));
assertTrue(Pattern.matches(".*", "any string"));

捕獲分組

支持用小括號 () 將模式分組,Matcher 提供 group 方法獲取分組的內容

符號 含義
(pattern) 匹配分組並且捕獲子表達式
(?:pattern) 匹配分組但是不捕獲子表達式

使用 groupCount 獲取捕獲分組的數量,由於下面例子中模式串中沒有小括號,所以沒有捕獲的分組

Pattern pattern = Pattern.compile("^[a-z0-9]+@[a-z0-9.]+[.][a-z]{2,4}$");
Matcher matcher = pattern.matcher("[email protected]");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 0);

使用 group 方法獲取分組的內容,分組的編號以左括號爲準,gruop(i) 返回第 i 個分組,group(0) 代表整個匹配串

Pattern pattern = Pattern.compile("^([a-z0-9]+)@(([a-z0-9.]+)[.]([a-z]{2,4}))$");
Matcher matcher = pattern.matcher("[email protected]");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 4);
assertEquals(matcher.group(), "[email protected]");
assertEquals(matcher.group(1), "hatlonely");
assertEquals(matcher.group(2), "foxmail.com");
assertEquals(matcher.group(3), "foxmail");
assertEquals(matcher.group(4), "com");

可以用 ?: 阻止捕獲,如下面代碼所示,foxmail.com 就沒有被捕獲

Pattern pattern = Pattern.compile("^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$");
Matcher matcher = pattern.matcher("[email protected]");
assertTrue(matcher.matches());
assertEquals(matcher.groupCount(), 3);
assertEquals(matcher.group(), "[email protected]");
assertEquals(matcher.group(0), "[email protected]");
assertEquals(matcher.group(1), "hatlonely");
assertEquals(matcher.group(2), "foxmail");
assertEquals(matcher.group(3), "com");

預測

符號 含義
(?=pattern) 正向預測,非捕獲匹配,匹配結束
(?!pattern) 反向預測,非捕獲匹配,匹配結束
(?<=pattern) 正向預測,非捕獲匹配,匹配開始
(?<!pattern) 反向預測,非捕獲匹配,匹配開始

正向預測是指匹配開始或者結束的字符串需要匹配(或者反向預測不能匹配)分組,分組內的模式僅用於預測,不會出現在最終的匹配串中,這種匹配只能用於部分匹配,?=?! 匹配開始,?<=?<! 匹配結束

{
    Pattern pattern = Pattern.compile("Windows (?=95|98|NT|2000)");
    Matcher matcher = pattern.matcher("Windows 2000");
    assertTrue(matcher.find());
    assertEquals(matcher.group(), "Windows ");
    assertEquals(matcher.groupCount(), 0);
}
{
    Pattern pattern = Pattern.compile("Windows (?!95|98|NT|2000)");
    Matcher matcher = pattern.matcher("Windows vista");
    assertTrue(matcher.find());
    assertEquals(matcher.group(), "Windows ");
    assertEquals(matcher.groupCount(), 0);
}
{
    Pattern pattern = Pattern.compile("(?<=95|98|NT|2000) Windows");
    Matcher matcher = pattern.matcher("2000 Windows");
    assertTrue(matcher.find());
    assertEquals(matcher.group(), " Windows");
    assertEquals(matcher.groupCount(), 0);
}
{
    Pattern pattern = Pattern.compile("(?<!95|98|NT|2000) Windows");
    Matcher matcher = pattern.matcher("vista Windows");
    assertTrue(matcher.find());
    assertEquals(matcher.group(), " Windows");
    assertEquals(matcher.groupCount(), 0);
}

反向引用

符號 含義
\1 匹配捕獲匹配到的反向引用,\2 表示第二個反向引用

前面通過捕獲獲得的分組可以再後面引用,比如 (\w+) \1 表示兩個重複的單詞

assertTrue(Pattern.matches("(\\w+) \\1", "ab ab"));
assertTrue(Pattern.matches("(\\w+) \\1", "abc abc"));
assertFalse(Pattern.matches("(\\w+) \\1", "abc def"));

正則替換

StringPattern 都提供了 replaceAllreplaceFirst 方法來作正則替換,替換串中可以 $i 來獲取正則匹配捕獲的分組

Pattern pattern = Pattern.compile("^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$");
assertEquals(pattern.matcher("[email protected]").replaceAll(
        "$0 $1 $2 $3"
), "[email protected] hatlonely foxmail com");

assertEquals("[email protected]".replaceAll(
        "^([a-z0-9]+)@(?:([a-z0-9.]+)[.]([a-z]{2,4}))$", "$0 $1 $2 $3"
), "[email protected] hatlonely foxmail com");

搜索所有匹配

每次 find 調用都會匹配目標串中的一個匹配,通過多次 find 調用,可以找出目標串中所有的匹配

String str = "abab x acac y aeae";
Pattern pattern = Pattern.compile("(\\w+)\\1");
Matcher matcher = pattern.matcher(str);

List<String> li = new ArrayList<>();
while (matcher.find()) {
    li.add(matcher.group());
    assertThat(str.substring(matcher.start(), matcher.end()), equalTo(matcher.group()));
}
assertThat(li, equalTo(List.of("abab", "acac", "aeae")));

鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章