Java 正規表達式 (Regular Expressions)

正規表達式用來匹配和處理字串模式。

基本使用

String 的正規方法

String str = "Hello World";

// matches:完全匹配
str.matches("Hello.*");  // true

// replaceAll:取代
str.replaceAll("\\s+", "-");  // "Hello-World"

// replaceFirst:取代第一個
str.replaceFirst("o", "0");  // "Hell0 World"

// split:分割
"a,b,c".split(",");  // ["a", "b", "c"]

Pattern 和 Matcher

import java.util.regex.*;

String text = "The cat sat on the mat";
String regex = "\\b\\w{3}\\b";  // 3 個字母的單字

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);

while (matcher.find()) {
    System.out.println(matcher.group());  // The, cat, sat, the, mat
}

常用語法

字元類別

語法說明
.任意字元
\d數字 [0-9]
\D非數字
\w單字字元 [a-zA-Z0-9_]
\W非單字字元
\s空白字元
\S非空白字元
[abc]a, b, 或 c
[^abc]非 a, b, c
[a-z]a 到 z

量詞

語法說明
*0 或多個
+1 或多個
?0 或 1 個
{n}剛好 n 個
{n,}至少 n 個
{n,m}n 到 m 個

錨點

語法說明
^行首
$行尾
\b單字邊界
\B非單字邊界

群組

語法說明
(...)捕獲群組
(?:...)非捕獲群組
\1反向參照

Pattern 方法

// 編譯
Pattern pattern = Pattern.compile("\\d+");

// 帶旗標編譯
Pattern patternCI = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);

// 快速匹配
Pattern.matches("\\d+", "12345");  // true

// 分割
String[] parts = pattern.split("a1b2c3");  // ["a", "b", "c"]

旗標

Pattern.CASE_INSENSITIVE  // 忽略大小寫
Pattern.MULTILINE         // 多行模式
Pattern.DOTALL           // . 匹配換行
Pattern.UNICODE_CASE     // Unicode 大小寫

Matcher 方法

Matcher matcher = pattern.matcher(text);

matcher.matches();   // 完全匹配
matcher.find();      // 尋找下一個
matcher.group();     // 取得匹配的字串
matcher.group(1);    // 取得群組 1
matcher.start();     // 匹配的開始位置
matcher.end();       // 匹配的結束位置
matcher.reset();     // 重設

實際應用

驗證 Email

public static boolean isValidEmail(String email) {
    String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
    return email.matches(regex);
}

isValidEmail("user@example.com");  // true
isValidEmail("invalid");           // false

驗證電話號碼

public static boolean isValidPhone(String phone) {
    String regex = "^09\\d{8}$";  // 台灣手機
    return phone.matches(regex);
}

提取數字

String text = "價格是 100 元,數量 5 個";
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);

List<Integer> numbers = new ArrayList<>();
while (matcher.find()) {
    numbers.add(Integer.parseInt(matcher.group()));
}
// [100, 5]

提取 URL

String text = "訪問 https://example.com 或 http://test.org";
String regex = "https?://[\\w.-]+(?:/[\\w.-]*)*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);

while (matcher.find()) {
    System.out.println(matcher.group());
}
// https://example.com
// http://test.org

取代

// 隱藏電話號碼
String phone = "0912345678";
String masked = phone.replaceAll("(\\d{4})\\d{4}(\\d{2})", "$1****$2");
// "0912****78"

// 移除 HTML 標籤
String html = "<p>Hello <b>World</b></p>";
String text = html.replaceAll("<[^>]+>", "");
// "Hello World"

使用群組

String log = "2024-12-10 14:30:45 INFO User logged in";
String regex = "(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (\\w+) (.+)";

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(log);

if (matcher.matches()) {
    String date = matcher.group(1);    // 2024-12-10
    String time = matcher.group(2);    // 14:30:45
    String level = matcher.group(3);   // INFO
    String message = matcher.group(4); // User logged in
}

命名群組 (Java 7+)

String regex = "(?<date>\\d{4}-\\d{2}-\\d{2}) (?<time>\\d{2}:\\d{2}:\\d{2})";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher("2024-12-10 14:30:45");

if (matcher.matches()) {
    String date = matcher.group("date");  // 2024-12-10
    String time = matcher.group("time");  // 14:30:45
}

注意事項

跳脫字元

在 Java 字串中,\ 需要跳脫:

// 匹配數字
String regex = "\\d+";  // Java 字串中用 \\d

// 匹配反斜線
String regex2 = "\\\\";  // 匹配一個 \

效能

重複使用的正規表達式應該預先編譯:

// 不好:每次都編譯
for (String s : strings) {
    if (s.matches("\\d+")) { }
}

// 好:預先編譯
Pattern pattern = Pattern.compile("\\d+");
for (String s : strings) {
    if (pattern.matcher(s).matches()) { }
}