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

Java 正則表達式完全指南:從語法基礎到 Jakarta-ORO 實戰應用

     編輯:Java 相關知識

正則表達式是文本處理的「瑞士軍刀」。Java 1.4 前無原生支持,開發者依賴 Jakarta-ORO 等第三方庫;Java 1.4+ 引入 java.util.regex 套件,提供標準 API。本文涵蓋正則語法核心、Java 原生 API 實戰、經典應用場景(日誌解析、HTML 處理、鏈接重寫),並對比新舊 API 差異。

一、正則表達式語法速查表

元字符名稱含義示例匹配
.句點任意單字符(除換行符)t.n → tan, ten, tin, ton
[abc]字符類方括號內任一字符t[aeio]n → tan, ten, tin, ton
[^abc]否定字符類除方括號外任一字符t[^aeio]n → tbn, tcn...
\d數字簡寫[0-9]
\D非數字[^0-9]
\w單詞字符[a-zA-Z0-9_]
\W非單詞字符[^a-zA-Z0-9_]
\s空白字符空格、Tab、換行、換頁
\S非空白字符
|或運算左右任一分支匹配cat|dog → cat 或 dog
()分組/捕獲提取子串、優先級控制(\d{4})-(\d{2}) 捕獲年月
(?:)非捕獲組僅分組不捕獲(?:\d{3}-){2}\d{4}
*零或多次{0,}ab*c → ac, abc, abbc...
+一次或多次{1,}ab+c → abc, abbc...
?零或一次{0,1}ab?c → ac, abc
{n}精確 n 次\d{4} → 4 位數字
{n,}至少 n 次\d{2,} → 2 位以上數字
{n,m}n 到 m 次\d{3,5} → 3~5 位數字
^行首/字符串首多行模式下匹配行首^Start
$行尾/字符串尾多行模式下匹配行尾End$
\b單詞邊界\bcat\b 匹配單詞 cat
\B非單詞邊界

二、Java 原生 API (java.util.regex) 核心類

import java.util.regex.Pattern;
import java.util.regex.Matcher;

// 1. 編譯模式(線程安全,可復用)
Pattern pattern = Pattern.compile(regex);                    // 大小寫敏感
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); // 忽略大小寫

// 2. 創建匹配器(非線程安全,每次匹配 new)
Matcher matcher = pattern.matcher(inputString);

// 3. 核心匹配方法
boolean matches()        // 整串精確匹配
boolean lookingAt()      // 前綴匹配
boolean find()           // 子串搜尋(可重複調用遍歷所有匹配)
boolean find(int start)  // 從指定位置搜尋

// 4. 組提取
String group()           // 整個匹配
String group(int i)      // 第 i 組 (1-based)
int groupCount()         // 組數量

// 5. 替換操作
String replaceAll(String replacement)      // 替換所有匹配
String replaceFirst(String replacement)    // 替換首個匹配
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
    matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb); // 獲取完整結果 sb.toString()

// 6. 常用靜態工具方法
Pattern.matches(regex, input)              // 一行代碼精確匹配
String replaced = input.replaceAll(regex, replacement) // String 便利方法

三、經典實戰場景

場景 1:Web 日誌解析 —— 提取 IP 與時間戳

日誌格式:192.168.1.100 - - [29/Sep/2001:18:50:09 +0800] "GET /index.html HTTP/1.1" 200 1234

String logRegex = "(\\d{1,3}(?:\\.\\d{1,3}){3})\\s-\\s-\\s\\[([^\\]]+)\\]";
// 組1: IP地址  組2: 時間戳

Pattern p = Pattern.compile(logRegex);
Matcher m = p.matcher(logLine);
if (m.find()) {
    String ip = m.group(1);
    String timestamp = m.group(2);
    // 處理業務邏輯...
}

場景 2:HTML 屬性提取 —— 解析 <font> 標籤屬性

// 兩步法:先提取標籤整體屬性串,再拆分名值對
String fontTagRegex = "<font\\s+([^>]+)>";           // 組1: 所有屬性
String attrRegex = "(\\w+)\\s*=\\s*\"([^\"]*)\"";      // 組1:名 組2:值

Pattern tagPattern = Pattern.compile(fontTagRegex, Pattern.CASE_INSENSITIVE);
Pattern attrPattern = Pattern.compile(attrRegex);

Matcher tagMatcher = tagPattern.matcher(htmlLine);
while (tagMatcher.find()) {
    String attrs = tagMatcher.group(1); // face="Arial" size="+2" color="red"
    Matcher attrMatcher = attrPattern.matcher(attrs);
    while (attrMatcher.find()) {
        System.out.println(attrMatcher.group(1) + " = " + attrMatcher.group(2));
    }
}

場景 3:鏈接重寫 —— 域名遷移

將 href="https://widgets.acme.com/..." 改為 href="https://newserver.acme.com/..."

String linkRegex = "(href\\s*=\\s*\")https://widgets\\.acme\\.com(.*?\")";
// 組1: 前綴  組2: 後綴
String replacement = "$1https://newserver.acme.com$2";

String result = input.replaceAll(linkRegex, replacement);
// $1 $2 為組引用,對應 replacement 中的 $1 $2

四、Jakarta-ORO 與 java.util.regex 對比遷移

維度Jakarta-ORO (舊)java.util.regex (標準)
包名com.oroinc.text.regex.*java.util.regex.*
編譯器PatternCompiler + Perl5CompilerPattern.compile()
模式對象Pattern (接口)Pattern (final 類)
匹配器PatternMatcher + Perl5MatcherMatcher
匹配方法matches()、contains()matches()、find()
組提取MatchResult.getGroup(i)matcher.group(i)
替換Util.substitute() + Perl5Substitutionmatcher.replaceAll() / String.replaceAll()
Perl 5 兼容性完全兼容高度兼容(少數高級特性差異)
性能優化較好JDK 內建,持續優化
依賴需引入第三方 JAR零依賴,JDK 內建

遷移代碼對照

// ORO 寫法
PatternCompiler compiler = new Perl5Compiler();
Pattern pattern = compiler.compile(regex, Perl5Compiler.CASE_INSENSITIVE_MASK);
PatternMatcher matcher = new Perl5Matcher();
if (matcher.contains(input, pattern)) {
    MatchResult result = matcher.getMatch();
    String group1 = result.getGroup(1);
}

// 標準 API 寫法(推薦)
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
    String group1 = matcher.group(1);
}

五、性能優化與最佳實踐

  1. 預編譯 Pattern 復用:Pattern 線程安全,作為常量/靜態字段緩存
  2. 避免過度捕獲:不需要提取的組用 (?:...) 非捕獲組
  3. 錨定優化:確定位置用 ^ $ \b 減少回溯
  4. 貪婪 vs 非貪婪:.*? 非貪婪避免過度匹配,但增加回溯開銷,按需選擇
  5. 預編譯標志位:Pattern.CASE_INSENSITIVE、Pattern.MULTILINE、Pattern.DOTALL(讓 . 匹配換行)
  6. 複雜正則拆解:超過 50 字符的正則拆分為多步驟,可讀性與維護性雙贏

六、常見陷阱

陷阱原因修正
Java 字符串雙重轉義"\\d+" → 正則引擎見 \d+習慣雙寫反斜杠;Java 13+ 可用文本塊 """\\d+"""
matches() 需整串匹配Pattern.matches("\\d+", "abc123") 返回 false用 matcher.find() 或 ".*\\d+.*"
分組索引從 1 開始group(0) 為整匹配切記 group(1) 是首個捕獲組
替換中 $ 需轉義replaceAll(".", "$1") 會按組引用解析用 Matcher.quoteReplacement() 或雙寫 \\$

總結:正則表達式威力巨大,但「寫時易讀,讀時難寫」。掌握核心語法、善用 Pattern/Matcher 標準 API、建立常用正則片段庫(郵箱、手機、身份證、URL、日期等),並遵循「預編譯、非捕獲、錨定、拆解」原則,能高效解決 90% 文本處理需求。現代 Java 專案棄用 Jakarta-ORO,全面擁抱 java.util.regex。

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