正則表達式是處理字符串匹配、提取、替換、分割的利器。Java 通過 java.util.regex 包提供 Pattern(編譯後的模式)、Matcher(匹配引擎)兩大核心類,支援 Perl 風格語法。本文系統梳理語法元字符、分組捕獲、貪婪/非貪婪模式、常用 API,並給出典型實戰場景代碼。
// 1. 編譯模式(線程安全,可重用)
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); // 忽略大小寫
// 2. 創建匹配器(非線程安全,每次匹配 new)
Matcher matcher = pattern.matcher(input);
// 3. 執行匹配操作
boolean found = matcher.find(); // 部分匹配,尋找下一個
boolean matches = matcher.matches(); // 整體匹配
boolean looking = matcher.lookingAt(); // 從開頭匹配
最佳實踐:Pattern 為不可變線程安全,建議定義為 static final 常量複用;Matcher 有狀態,不可共享。
| 類別 | 符號 | 說明 | 示例 |
|---|---|---|---|
| 字符類 | . | 任意單字符(除換行) | a.c 匹配 abc、axc |
\d | 數字 [0-9] | \d{3} 三位數 | |
\w | 單詞字符 [a-zA-Z0-9_] | \w+ 匹配標識符 | |
| 量詞(貪婪) | * | 0 次或多次 {0,} | ab*c 匹配 ac、abc、abbc |
+ | 1 次或多次 {1,} | \d+ 至少一位數字 | |
? | 0 次或 1 次 {0,1} | colou?r 匹配 color/colour | |
| 量詞(精確) | {n} | 恰好 n 次 | \d{11} 手機號長度 |
{n,} | 至少 n 次 | \w{6,} 密碼最短 6 位 | |
{n,m} | n 到 m 次 | \d{3,5} 3-5 位數字 | |
| 量詞(非貪婪) | *? +? ?? {n,m}? | 儘可能少匹配 | .*? 匹配最短字符串 |
| 邊界 | ^ | 行首(多行模式下) | ^Start |
$ | 行尾 | End$ | |
\b | 單詞邊界 | \bcat\b 單詞 cat | |
| 分組與引用 | () | 捕獲組,編號 1,2... | (\d{4})-(\d{2}) |
(?:) | 非捕獲組 | (?:\d{3}){2} 不佔組號 | |
\1 \2 | 反向引用 | (\w+)\s+\1 重複單詞 | |
| 斷言 | (?=) | 正向前瞻 | \w+(?=\d) 後面跟數字的單詞 |
(?!) | 負向前瞻 | \w+(?!\d) 後面不跟數字 | |
(?<=) (? | 正/負向後顧 | (?<=\$)\d+ $ 後的數字 | |
| 或運算 | | | 分支 | cat|dog 貓或狗 |
| 字符類 | [] | 集合 | [aeiou] 元音 |
| 排除類 | [^] | 非集合 | [^0-9] 非數字 |
Pattern.CASE_INSENSITIVE // 忽略大小寫 (?i)
Pattern.MULTILINE // 多行模式 ^$ 匹配行首尾 (?m)
Pattern.DOTALL // . 匹配含換行 (?s)
Pattern.UNICODE_CASE // Unicode 大小寫折疊 (?u)
Pattern.COMMENTS // 允許註解和空白 (?x)
Pattern.UNICODE_CHARACTER_CLASS // \w \d 等支援 Unicode (?U)
可組合:Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE)
String text = "User: alice, Id: 1001; User: bob, Id: 1002";
Pattern p = Pattern.compile("User: (\\w+), Id: (\\d+)");
Matcher m = p.matcher(text);
while (m.find()) {
String name = m.group(1); // 分組 1
String id = m.group(2); // 分組 2
System.out.println(name + " -> " + id);
}
// 輸出: alice -> 1001, bob -> 1002
// 簡單替換所有數字為 *
String masked = text.replaceAll("\\d", "*"); // 內部用 Pattern/Matcher
// 進階替換:用分組內容構建新字符串
String swapped = m.replaceAll("ID=$2, Name=$1"); // 使用 $n 引用分組
// 或使用函數式替換
String custom = p.matcher(text).replaceAll(mr ->
"【" + mr.group(1).toUpperCase() + "】");
String csv = "apple, banana; orange|pear";
// 按多種分隔符分割
String[] fruits = csv.split("[,;|]\\s*");
// 結果: [apple, banana, orange, pear]
// 手機號校驗
boolean valid = Pattern.matches("1[3-9]\\d{9}", "13800138000"); // true
// 郵箱簡易校驗
boolean emailOk = Pattern.matches("^[\\w.-]+@[\\w.-]+\\.\\w{2,}$", "test@example.com");
String html = "HelloWorld";
// 貪婪:匹配整個字符串
Pattern.compile(".*").matcher(html).find(); // HelloWorld
// 非貪婪:匹配單個標籤
Pattern.compile(".*?").matcher(html).find(); // Hello
// 錯誤:循環中重複編譯
for (String s : list) { if (s.matches("\\d+")) ... }
// 正確:靜態常量複用
private static final Pattern DIGITS = Pattern.compile("\\d+");
for (String s : list) { if (DIGITS.matcher(s).matches()) ... }
用戶輸入直接拼接進正則可能導致 ReDoS(正則拒絕服務)或邏輯繞過。嚴禁:Pattern.compile(userInput)。需對用戶輸入用 Pattern.quote() 轉義,或使用白名單校驗。
Java 不支援不定寬度後顧斷言:(?<=ab*) 報錯。需改寫為定寬或用其他邏輯。
| 場景 | 正則 | 說明 |
|---|---|---|
| 手機號(大陸) | ^1[3-9]\\d{9}$ | 11 位,以 1 開頭,第二位 3-9 |
| 身份證 18 位 | ^\\d{17}[\\dXx]$ | 簡易版,嚴格需校驗校驗碼 |
^[\\w.-]+@[\\w.-]+\\.\\w{2,}$ | 通用簡易版 | |
| URL | ^https?://[\\w.-]+(?:\\.[\\w.-]+)+(?:[/?#][^\\s]*)?$ | HTTP/HTTPS |
| IPv4 | ^((25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d?\\d)$ | 精確校驗 |
| 中文字符 | [\\u4e00-\\u9fa5] | 基本漢字區塊 |
| 日期 yyyy-MM-dd | ^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$ | 格式校驗,邏輯合法性需代碼判斷 |
| 密碼強度 | ^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$ | 大小寫+數字+特殊字符 8 位以上 |
Java 正則表達式通過 Pattern/Matcher 提供強大的字符串處理能力。掌握核心語法、量詞貪婪性、分組捕獲、斷言、編譯標誌,並養成預編譯 Pattern、警惕 ReDoS、善用非貪婪模式的習慣,能高效解決 95% 的文本解析需求。複雜業務建議結合 String 原生方法、第三方庫(如 Apache Commons Lang StringUtils)協同使用。