背景:JDK 1.4 引入原生正則支援
在此之前,Java 僅能靠 StringTokenizer、charAt、substring 手工解析文本,代碼冗長且脆弱。JDK 1.4 新增 java.util.regex,提供 Perl 風味的正則引擎,三大核心類:
- Pattern:編譯後的正則表達式,不可變,線程安全
- Matcher:針對輸入序列的匹配引擎,非線程安全
- PatternSyntaxException:編譯錯誤異常
正則語法速查表
| 構造 | 匹配內容 |
. | 除換行外任意字符 |
\d | 數字 [0-9] |
\D | 非數字 [^0-9] |
\s | 空白 [ \t\n\x0B\f\r] |
\S | 非空白 |
\w | 單詞字符 [a-zA-Z_0-9] |
\W | 非單詞字符 |
[abc] | a 或 b 或 c |
[^abc] | 除 a/b/c 外任意字符 |
[a-zA-Z] | 字母範圍 |
^ | 行首(MULTILINE 模式) |
$ | 行尾(MULTILINE 模式) |
* + ? {n,m} | 量詞:0+/1+/0-1/n-m 次 |
(...) | 捕獲組,可用 $1 或 group(1) 反向引用 |
核心 API 用法
Pattern 編譯與分割
Pattern p = Pattern.compile("[,\\s]+"); // 逗號或空白分隔
String[] tokens = p.split("one,two, three four , five");
// 結果:["one", "two", "three", "four", "five"]
Matcher 三種匹配模式
| 方法 | 行為 | 典型場景 |
matches() | 整個輸入完全匹配 | 驗證 Email、電話格式 |
lookingAt() | 從開頭匹配 | 檢查前綴 |
find() | 掃描尋找下一匹配 | 提取所有單詞、標籤 |
替換操作
Matcher m = p.matcher(input);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "replacement"); // 可引用組 $1 $2
}
m.appendTail(sb); // 複制剩餘尾部
String result = sb.toString();
四大實戰範例
1. 簡單單詞替換
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat, two cats in the yard");
// 循環 appendReplacement("dog") → "one dog, two dogs in the yard"
2. Email 格式驗證與清洗
// 禁止以 . 或 @ 開頭
Pattern p1 = Pattern.compile("^\\.|^@");
// 禁止 www. 開頭
Pattern p2 = Pattern.compile("^www\\.");
// 移除非法字符(僅保留字母數字 . @ _ - ~ #)
Pattern p3 = Pattern.compile("[^A-Za-z0-9\\.@_\\-~#]+");
Matcher m = p3.matcher(email);
String clean = m.replaceAll(""); // 刪除非法字符
3. 刪除檔案中的控制字符
Pattern p = Pattern.compile("\\p{Cntrl}"); // POSIX 控制字符類
Matcher m = p.matcher("");
try (BufferedReader in = ...; BufferedWriter out = ...) {
String line;
while ((line = in.readLine()) != null) {
m.reset(line);
out.write(m.replaceAll(""));
out.newLine();
}
}
4. 從 Java 源碼提取單行註釋
Pattern p = Pattern.compile("//.*$", Pattern.MULTILINE);
// 使用 NIO 內存映射讀取大檔案
FileChannel fc = new FileInputStream("Replacement.java").getChannel();
ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
CharBuffer cb = Charset.forName("8859_1").newDecoder().decode(bb);
Matcher m = p.matcher(cb);
while (m.find()) System.out.println("Found: " + m.group());
CharSequence 介面:統一輸入源
Matcher 接受 CharSequence,標準實現包括 String、StringBuffer、CharBuffer(NIO),也可自行實現以支援資料庫 CLOB、網路流等任意字符源。
最佳實踐提示
- 預編譯複用:
Pattern 不可變且編譯昂貴,靜態常量或緩存複用。
- 原始字符串:Java 13+ 可用
"""regex""" 文本塊避免雙重轉義。
- 貪婪 vs 勉強:
.*? 非貪婪匹配,避免過度回溯導致性能災難。
- 預編譯標誌:
Pattern.CASE_INSENSITIVE、MULTILINE、DOTALL(讓 . 匹配換行)、UNICODE_CASE。
- 上一篇文章: Java 打印不再難:DocumentRenderer 輕鬆實現高質量文檔列印
- 下一篇文章: 測試分類策略:打造敏捷構建流程,解決測試套件執行緩慢難題
Copyright ©
程式碼之家 All Rights Reserved