Java正則表達式完全指南
- 一、正則表達式基礎概念
- 1.1 什么是正則表達式
- 1.2 Java中的正則表達式支持
- 二、正則表達式基本語法
- 2.1 普通字符
- 2.2 元字符
- 2.3 預定義字符類
- 三、Java中正則表達式的基本用法
- 3.1 編譯正則表達式
- 3.2 創建Matcher對象并執行匹配
- 3.3 常用的Matcher方法
- 四、正則表達式高級應用
- 4.1 分組與捕獲
- 4.2 反向引用
- 4.3 貪婪匹配與非貪婪匹配
- 4.4 零寬斷言
- 五、正則表達式在實際開發中的應用
- 5.1 表單驗證
- 郵箱驗證
- 手機號驗證
- 身份證號碼驗證
- 5.2 文本替換
- 替換HTML標簽
- 敏感詞過濾
- 5.3 文本分割
- 按逗號或空格分割
- 按數字分割
- 六、正則表達式性能優化
- 七、常見問題與注意事項
- 總結
正則表達式(Regular Expression,簡稱Regex)是一種強大的文本處理工具,它可以幫助開發者高效地進行字符串匹配、查找、替換和分割等操作。在Java中正則表達式的應用場景極為廣泛,從簡單的表單驗證到復雜的文本解析,都離不開正則表達式的支持。雖然之前我也講過正則表達式,但過于通用,今天本文將專門全面介紹Java中正則表達式的相關知識,從基礎語法到高級應用,并結合豐富實例代碼,帶你深入理解和掌握這一強大工具。
一、正則表達式基礎概念
1.1 什么是正則表達式
正則表達式是一種由字符和特殊符號組成的模式,用于描述字符串的特定格式規則。通過使用正則表達式,可以:
- 檢查字符串是否符合特定格式(如郵箱、手機號)
- 從文本中提取感興趣的內容(如URL、數字)
- 替換文本中的特定部分
- 將文本按特定規則分割
1.2 Java中的正則表達式支持
Java通過java.util.regex
包提供對正則表達式的支持,主要涉及以下三個類:
Pattern
類:用于編譯正則表達式,將正則表達式字符串編譯為模式對象。Matcher
類:用于執行匹配操作,對輸入字符串進行解釋和匹配操作。PatternSyntaxException
類:用于處理正則表達式語法錯誤的異常類。
二、正則表達式基本語法
2.1 普通字符
普通字符包括沒有顯式指定為元字符的所有可打印和不可打印字符,它們直接匹配自身。例如:
abc
匹配字符串 “abc”123
匹配字符串 “123”
2.2 元字符
元字符是正則表達式中具有特殊含義的字符,常用的元字符及其含義如下:
元字符 | 描述 |
---|---|
. | 匹配除換行符以外的任意字符 |
^ | 匹配字符串的開始位置 |
$ | 匹配字符串的結束位置 |
* | 匹配前面的子表達式零次或多次 |
+ | 匹配前面的子表達式一次或多次 |
? | 匹配前面的子表達式零次或一次 |
{n} | 匹配前面的子表達式恰好n次 |
{n,} | 匹配前面的子表達式至少n次 |
{n,m} | 匹配前面的子表達式至少n次,至多m次 |
[] | 匹配方括號中指定的任意一個字符 |
[^] | 匹配不在方括號中指定的任意一個字符 |
() | 標記一個子表達式的開始和結束位置 |
| | 表示或關系,匹配兩個或多個選項之一 |
2.3 預定義字符類
為了簡化常用字符類的定義,Java提供了一些預定義字符類:
預定義字符類 | 等價表達式 | 描述 |
---|---|---|
\d | [0-9] | 匹配一個數字字符 |
\D | [^0-9] | 匹配一個非數字字符 |
\w | [a-zA-Z_0-9] | 匹配一個單詞字符(字母、數字、下劃線) |
\W | [^a-zA-Z_0-9] | 匹配一個非單詞字符 |
\s | [ \t\n\x0B\f\r] | 匹配一個空白字符(空格、制表符、換行符等) |
\S | [^ \t\n\x0B\f\r] | 匹配一個非空白字符 |
三、Java中正則表達式的基本用法
3.1 編譯正則表達式
在Java中使用正則表達式,首先需要將正則表達式字符串編譯為Pattern
對象:
import java.util.regex.Pattern;public class RegexExample {public static void main(String[] args) {// 編譯正則表達式Pattern pattern = Pattern.compile("a.*c");}
}
3.2 創建Matcher對象并執行匹配
編譯后的Pattern
對象用于創建Matcher
對象,然后通過Matcher
對象執行匹配操作:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexExample {public static void main(String[] args) {// 編譯正則表達式Pattern pattern = Pattern.compile("a.*c");// 創建Matcher對象Matcher matcher = pattern.matcher("abc");// 執行匹配操作boolean isMatch = matcher.matches();System.out.println("是否匹配: " + isMatch); // 輸出: true}
}
3.3 常用的Matcher方法
matches()
:嘗試將整個輸入序列與模式匹配。find()
:在輸入序列中查找下一個匹配的子序列。group()
:返回當前匹配的子序列。start()
:返回當前匹配的子序列的起始索引。end()
:返回當前匹配的子序列的結束索引加1。
示例代碼:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class RegexExample {public static void main(String[] args) {String input = "Hello, world! Hello, Java!";Pattern pattern = Pattern.compile("Hello");Matcher matcher = pattern.matcher(input);// 查找所有匹配項while (matcher.find()) {System.out.println("匹配到: " + matcher.group() + ", 起始位置: " + matcher.start() + ", 結束位置: " + matcher.end());}}
}
輸出結果:
匹配到: Hello, 起始位置: 0, 結束位置: 5
匹配到: Hello, 起始位置: 14, 結束位置: 19
四、正則表達式高級應用
4.1 分組與捕獲
使用圓括號()
可以將正則表達式中的部分內容分組,每個分組可以被單獨捕獲和引用。分組編號從1開始,0表示整個匹配結果。
示例:匹配并提取郵箱地址中的用戶名和域名
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class GroupExample {public static void main(String[] args) {String email = "test.user@example.com";String regex = "([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(email);if (matcher.matches()) {System.out.println("完整匹配: " + matcher.group(0)); // 整個匹配結果System.out.println("用戶名: " + matcher.group(1)); // 第一組System.out.println("域名: " + matcher.group(2)); // 第二組}}
}
輸出結果:
完整匹配: test.user@example.com
用戶名: test.user
域名: example.com
4.2 反向引用
在正則表達式中,可以使用\n
(n為數字)引用前面已經捕獲的分組。例如,匹配重復的單詞:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class BackreferenceExample {public static void main(String[] args) {String text = "hello hello world world";String regex = "\\b(\\w+)\\s+\\1\\b";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);while (matcher.find()) {System.out.println("重復的單詞: " + matcher.group());}}
}
輸出結果:
重復的單詞: hello hello
重復的單詞: world world
4.3 貪婪匹配與非貪婪匹配
- 貪婪匹配:默認情況下,正則表達式的量詞(如
*
、+
、{n,m}
)是貪婪的,會盡可能多地匹配字符。 - 非貪婪匹配:在量詞后面加上
?
,可以將貪婪匹配轉換為非貪婪匹配,盡可能少地匹配字符。
示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class GreedyVsNonGreedy {public static void main(String[] args) {String text = "<html><body><h1>Hello</h1></body></html>";// 貪婪匹配String greedyRegex = "<.*>";Pattern greedyPattern = Pattern.compile(greedyRegex);Matcher greedyMatcher = greedyPattern.matcher(text);if (greedyMatcher.find()) {System.out.println("貪婪匹配: " + greedyMatcher.group());}// 非貪婪匹配String nonGreedyRegex = "<.*?>";Pattern nonGreedyPattern = Pattern.compile(nonGreedyRegex);Matcher nonGreedyMatcher = nonGreedyPattern.matcher(text);while (nonGreedyMatcher.find()) {System.out.println("非貪婪匹配: " + nonGreedyMatcher.group());}}
}
輸出結果:
貪婪匹配: <html><body><h1>Hello</h1></body></html>
非貪婪匹配: <html>
非貪婪匹配: <body>
非貪婪匹配: <h1>
非貪婪匹配: </h1>
非貪婪匹配: </body>
非貪婪匹配: </html>
4.4 零寬斷言
零寬斷言用于在特定位置匹配某些內容,但不包含匹配的內容本身。Java支持四種零寬斷言:
斷言類型 | 語法 | 描述 |
---|---|---|
正向先行斷言 | (?=pattern) | 匹配后面跟著pattern的位置 |
負向先行斷言 | (?!pattern) | 匹配后面不跟著pattern的位置 |
正向后行斷言 | (?<=pattern) | 匹配前面是pattern的位置 |
負向后行斷言 | (?<!pattern) | 匹配前面不是pattern的位置 |
示例:匹配所有以"ing"結尾的單詞
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class LookaroundExample {public static void main(String[] args) {String text = "running jumping swimming";String regex = "\\b\\w+(?=ing\\b)";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(text);while (matcher.find()) {System.out.println("匹配到: " + matcher.group());}}
}
輸出結果:
匹配到: run
匹配到: jump
匹配到: swim
五、正則表達式在實際開發中的應用
5.1 表單驗證
正則表達式常用于表單驗證,確保用戶輸入的數據符合預期格式。
郵箱驗證
public static boolean isValidEmail(String email) {String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";return Pattern.matches(regex, email);
}
手機號驗證
public static boolean isValidPhone(String phone) {String regex = "^1[3-9]\\d{9}$";return Pattern.matches(regex, phone);
}
身份證號碼驗證
public static boolean isValidIdCard(String idCard) {String regex = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$";return Pattern.matches(regex, idCard);
}
5.2 文本替換
使用正則表達式可以方便地進行文本替換操作。
替換HTML標簽
public static String removeHtmlTags(String html) {String regex = "<[^>]+>";return html.replaceAll(regex, "");
}
敏感詞過濾
public static String filterSensitiveWords(String text) {String[] sensitiveWords = {"敏感詞1", "敏感詞2", "敏感詞3"};String regex = String.join("|", sensitiveWords);return text.replaceAll(regex, "***");
}
5.3 文本分割
使用正則表達式可以按復雜規則分割文本。
按逗號或空格分割
public static String[] splitText(String text) {String regex = "[,\\s]+";return text.split(regex);
}
按數字分割
public static String[] splitByNumbers(String text) {String regex = "\\d+";return text.split(regex);
}
六、正則表達式性能優化
- 編譯一次,多次使用:避免在循環中重復編譯相同的正則表達式,應將編譯后的
Pattern
對象緩存并復用。 - 簡化正則表達式:復雜的正則表達式會降低匹配效率,盡量使用簡單、明確的表達式。
- 避免過度使用回溯:貪婪匹配和反向引用可能導致大量回溯,影響性能。
- 優先使用String類的方法:對于簡單的字符串操作,如
startsWith()
、endsWith()
、indexOf()
等,應優先使用String類的方法,比正則表達式效率更高。
七、常見問題與注意事項
- 轉義字符問題:在Java字符串中使用正則表達式時,需要注意轉義字符。例如,匹配點號
.
需要寫成\\.
,匹配反斜杠\
需要寫成\\\\
。 - 性能問題:復雜的正則表達式可能導致性能問題,特別是在處理大量數據時。
- 邊界問題:使用
^
和$
時要注意是否需要匹配整個字符串,還是只需要匹配部分內容。 - Unicode支持:Java默認支持Unicode字符,但在處理非ASCII字符時需要特別注意。
總結
正則表達式是Java中強大的文本處理工具,掌握正則表達式的基本語法和Java中的使用方法,對于提高字符串處理效率和開發質量至關重要。本文從基礎概念入手,詳細介紹了正則表達式的語法、Java中的API使用、高級應用場景以及性能優化等方面的內容,希望你在今后熟練使用盡量掌握。