面對「檢查字符串是否包含某些字符」、「提取日誌中的 IP 和時間」、「脫敏手機號」、「按多種分隔符分割」等需求,手寫 if/for/charAt 不僅繁瑣易錯,還難以應對變化。Java java.util.regex 將「匹配規則」封裝為 Pattern 對象,配合 Matcher 引擎,實現聲明式字符串處理。本文通過遞進式案例,展示從簡單匹配到複雜提取替換的完整實戰流程。
傳統寫法:
String str = "For my money...";
boolean found = false;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == 'y' || c == 'f') { found = true; break; }
}
正則寫法:
Pattern p = Pattern.compile("[yf]"); // 字符類:y 或 f
boolean found = p.matcher(str).find(); // 一行搞定
思維轉變:不關心「怎麼遍歷」,只描述「長什麼樣」。
// 1. 寫正則字符串
String regex = "正則表達式";
// 2. 編譯成 Pattern(建議靜態常量複用)
Pattern pattern = Pattern.compile(regex);
// 3. 創建 Matcher 執行操作
Matcher matcher = pattern.matcher(targetString);
// 4. 根據需求調用不同方法
matcher.find(); // 部分匹配/查找下一個
matcher.matches(); // 整體匹配
matcher.group(1); // 獲取分組內容
matcher.replaceAll(replacement); // 替換
pattern.split(input); // 分割
Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
// 或內嵌標誌
Pattern p2 = Pattern.compile("(?i)java");
String config = "host=localhost port=8080 timeout=30";
Pattern p = Pattern.compile("(\\w+)=(\\S+)");
Matcher m = p.matcher(config);
while (m.find()) {
System.out.println(m.group(1) + " -> " + m.group(2));
}
// host -> localhost, port -> 8080, timeout -> 30
String log = "User[alice] login from IP(192.168.1.1) at '2024-06-15 10:30'";
Pattern p = Pattern.compile("[\\[\\(']([^\\]\\')]+[\\]\\')]"); // 簡化示例
// 更穩健:分別處理不同括號
Pattern bracket = Pattern.compile("\\[([^\\]]+)\\]");
Pattern paren = Pattern.compile("\\(([^)]+)\\)");
Pattern quote = Pattern.compile("'([^']+)'");
String phone = "13800138000";
String masked = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
// 結果: 138****8000
// 解析:分組 1=前3位,分組 2=後4位,替換模板 $1****$2
String html = "Hello World
";
String text = html.replaceAll("<[^>]+>", ""); // 移除所有標籤
// 結果: Hello World
// 注意:複雜 HTML 請用 Jsoup 等專業解析器
String line = "apple, banana; orange|pear grape";
String[] fruits = line.split("[,;|]\\s*"); // 正則分割
// [apple, banana, orange, pear, grape]
String pwdRegex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
boolean strong = Pattern.matches(pwdRegex, "Abc12345@"); // true
// 解析:4 個正向前瞻分別檢查小寫、大寫、數字、特殊符號
String date = "2024-06-15";
String converted = date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$2/$3/$1");
// 結果: 06/15/2024
String html = "HelloWorld";
// 貪婪 .* 匹配整個字符串
// 非貪婪 .*? 匹配單個標籤
Pattern p = Pattern.compile("(.*?)");
Matcher m = p.matcher(html);
while (m.find()) System.out.println(m.group(1));
// Hello
// World
String text = "price: 100, discount: 20";
Pattern p = Pattern.compile("(\\d+)");
String result = p.matcher(text).replaceAll(mr -> {
int val = Integer.parseInt(mr.group());
return String.valueOf(val * 2); // 所有數字乘 2
});
// 結果: price: 200, discount: 40
private static final Pattern P = Pattern.compile(...)。str.replace("old", "new")、分割 str.split(",") 內部已優化,無需手動 Pattern。(a+)+、(a|aa)* 等嵌套量詞,輸入 aaaaaaaaaaaa! 會導致指數級回溯(ReDoS)。Pattern.quote(userInput) 將任意字符串視為字面量,防止注入。Pattern.CASE_INSENSITIVE | Pattern.DOTALL 組合使用。Java 正則表達式以 Pattern/Matcher 為核心,提供「聲明規則、引擎執行」的強大範式。掌握字符類、量詞、分組捕獲、非貪婪、斷言、替換模板 $n、函數式替換等核心技能,配合預編譯複用、ReDoS 防範、轉義用戶輸入工程化習慣,能優雅高效解決絕大多數文本處理場景。記住:正則是瑞士軍刀,不是萬能鑰匙——複雜結構化文本請用專用解析器。