首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 正則表達式實戰總結:匹配、切割、替換三大核心操作與性能優化

     編輯:Java 程式設計入門

前言:正則在 Java 中的地位

正則表達式是處理文本模式匹配、提取、轉換的利器。Java 通過 java.util.regex 包(Pattern、Matcher)及 String 便利方法(matches、split、replaceAll/First)提供完整支持。本文聚焦三大核心操作場景,結合實戰案例、常見陷阱、效能優化與最佳實踐。

一、核心 API 體系

類/方法用途特點
Pattern.compile(regex)編譯正則為 Pattern 對象線程安全、可復用、支援標誌位
Pattern.matches(regex, input)一次性匹配整個字符串等價 Pattern.compile(regex).matcher(input).matches(),不復用
matcher(input)創建 Matcher 進行操作非線程安全、狀態化、支援 find/group/replace
String.matches(regex)全字符串匹配常用於驗證
String.split(regex, limit)切割字符串返回 String[],limit 控制結果長度
String.replaceAll(regex, repl)全量替換支援 $1 $2 回引用
String.replaceFirst(regex, repl)首次替換同上
Matcher.replaceAll/replaceFirst替換並獲取結果可在替換邏輯中動態計算
Matcher.appendReplacement/tail流式構建替換結果高效處理大文本

二、操作一:匹配

1. 全字符串精確匹配 String.matches()


// 驗證 QQ 號:首位 1-9,長度 5-11 位數字
String regex = "[1-9]\\d{4,10}";
String qq = "123456";
boolean valid = qq.matches(regex); // true

// 常見驗證正則
String emailRegex = "^[\\w.-]+@[\\w-]+(\\.[\\w-]+)+$";
String phoneRegex = "^1[3-9]\\d{9}$"; // 中國大陸手機
String idCardRegex = "^\\d{17}[\\dXx]$"; // 18 位身份證(簡化)

注意: matches() 默認匹配整個字符串,等同於 ^regex$,無需顯式加錨點。

2. 部分匹配/提取 Matcher.find() + group()


String text = "訂單號: NO20240115001, 金額: ¥123.45, 用戶: user_abc";
Pattern p = Pattern.compile("NO\\d{12}|¥\\d+\\.\\d{2}|user_\\w+");
Matcher m = p.matcher(text);
while (m.find()) {
    System.out.println("匹配: " + m.group()); // 依次輸出三個匹配
}

// 分組提取
Pattern p2 = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher m2 = p2.matcher("今天是 2024-01-15");
if (m2.find()) {
    System.out.println("年: " + m2.group(1)); // 2024
    System.out.println("月: " + m2.group(2)); // 01
    System.out.println("日: " + m2.group(3)); // 15
    System.out.println("全匹配: " + m2.group(0)); // 2024-01-15
}

3. 常用字符類與邊界

三、操作二:切割 String.split()

1. 基礎切割


String line = "apple, banana;orange | pear";
String[] fruits = line.split("[,;|]\\s*"); // 正則:逗號/分號/竖線 + 可選空白
// 結果: ["apple", "banana", "orange", "pear"]

2. limit 參數控制


String s = "a,b,c,d";
s.split(",");        // ["a", "b", "c", "d"] (尾部空串丟棄)
s.split(",", 2);     // ["a", "b,c,d"] (最多 2 段)
s.split(",", -1);    // ["a", "b", "c", "d", ""] (保留尾部空串)
s.split(",", 0);     // 同無參 (丟棄尾部空串)

CSV 解析時常用 split(",", -1) 保留空列。

3. 陷阱:正則元字符需轉義


// 錯誤
"1.2.3".split("."); // 結果: [] ( . 匹配任意字符)

// 正確
"1.2.3".split("\\.");      // 雙重轉義:Java 字符串 -> 正則引擎
"1.2.3".split(Pattern.quote(".")); // 推薦:自動轉義整個字符串

四、操作三:替換

1. 簡單替換


String s = "aa bb cc dd";
s.replaceAll("\\s+", "_"); // "aa_bb_cc_dd" (多空白合為一個下劃線)

2. 回引用替換:重組格式


// 日期格式轉換 yyyy-MM-dd -> dd/MM/yyyy
String date = "2024-01-15";
String converted = date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1");
// 結果: "15/01/2024"

// 隱藏手機中間四位
String phone = "13812345678";
String masked = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
// 結果: "138****5678"

3. 動態計算替換 Matcher.appendReplacement


String text = "價格: 100元, 200元, 300元";
Pattern p = Pattern.compile("(\\d+)元");
Matcher m = p.matcher(text);
StringBuffer sb = new StringBuffer();
while (m.find()) {
    int price = Integer.parseInt(m.group(1));
    double discounted = price * 0.8; // 八折
    m.appendReplacement(sb, String.format("%.1f元", discounted));
}
m.appendTail(sb);
System.out.println(sb); // "價格: 80.0元, 160.0元, 240.0元"

4. 移除疊詞/連續重複字符


String s = "hellooo woorrld";
String dedup = s.replaceAll("(.)\\1+", "$1"); // "helo world"
// (.) 捕獲任意字符,\\1+ 匹配同字符重複 1 次以上,替換為單個 $1

五、效能優化與避坑指南

1. 預編譯 Pattern 復用(高頻熱點必須)


// ❌ 低效:每次調用重新編譯
if (input.matches(regex)) { ... }

// ✅ 高效:靜態常量預編譯
private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
public boolean isValidEmail(String email) {
    return EMAIL_PATTERN.matcher(email).matches();
}

2. 避免災難性回溯


// 惡意輸入導致指數級回溯
String evil = "aaaaaaaaaaaaaaaaaaaaaaaaac";
String regex = "(a+)+b"; // 嵌套量詞
// Pattern.compile(regex).matcher(evil).matches(); // 極慢甚至卡死

// 優化:使用佔有優先量詞 / 原子組
String safeRegex = "(?>a+)b"; // 原子組,匹配失敗不回溯
// 或
String safeRegex2 = "a++b"; // 佔有優先量詞 (Java 支援)

原則:避免嵌套量詞、優先用具體字符類代替 .、關鍵路徑用佔有優先量詞 (?>...) 或 ++/*+。

3. 選擇正確方法

語法含義示例
\\d數字 [0-9]\\d{3} 三位數
\\D非數字
\\w單詞字符 [a-zA-Z0-9_]
\\W非單詞字符
\\s空白字符 (空格、制表、換行)\\s+ 多個空白
\\S非空白
.任意字符(除換行,單行模式除外)
^ $行首/行尾(多行模式)或字符串首/尾
\\b \\B單詞邊界/非邊界\\bcat\\b 匹配單詞 cat
(?=...) (?!...)正向/負向前瞻(零寬斷言)\\d+(?=元) 匹配「100」在「100元」中
(?<=...) (?<!...)正向/負向後顧(固定寬度)(?<=¥)\\d+ 匹配「¥100」中的「100」
場景推薦理由
驗證整個字符串格式String.matches() / Pattern.matches()語義清晰、自動錨定首尾
提取多個匹配/分組Matcher.find() + group()唯一可獲取分組信息
簡單切割String.split()便捷、返回數組
複雜切割/保留分隔符Matcher.find() 手動構建split 無法保留分隔符
簡單替換String.replaceAll()一行代碼
替換邏輯複雜/需動態計算Matcher.appendReplacement可編程控制替換內容
大文本流式處理Matcher.appendReplacement/tail避免全量字符串拷貝

4. 多線程安全

  • Pattern:線程安全,不可變,可作為 static final 共享。
  • Matcher:非線程安全,有狀態,不可共享,每線程創建或用 ThreadLocal。

六、現代替代:Java 11+ Pattern.asMatchPredicate() / asPredicate()


Predicate isEmail = Pattern.compile(EMAIL_REGEX).asMatchPredicate();
List emails = inputs.stream().filter(isEmail).collect(toList());

// Stream 配合正則
Stream lines = Files.lines(path);
lines.flatMap(line -> Pattern.compile("\\s+").splitAsStream(line))
     .filter(word -> word.length() > 3)
     .collect(toList());

結語

Java 正則三大操作——匹配驗證提取、切割分割、替換重組——覆蓋 90% 文本處理需求。掌握 Pattern/Matcher 核心 API、分組回引用、前瞻後顧、split limit 語義、appendReplacement 流式替換,並堅守預編譯復用、防災難性回溯、線程隔離 Matcher三大效能鐵律,即可寫出高效、健壯、可維護的正則代碼。複雜結構化文本(JSON、XML、HTML)請勿用正則硬解析,改用專用解析器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved