[Regex] 正規表示式指南 – Regular Expression Guide

正則表達式: Character | Quantifier | Grouping | Anchors | Character Range | Examples
範例: Grouping | Lookbehind

Intro

正規表示式 - Wiki

PCRE 表達式全集 - Wiki


正則表達式

1. 字元匹配基礎(Character Matching Basics)

語法 分類名稱 說明
. 通用字元匹配符 (Wildcard) 匹配任意一個字元(通常不含換行符)
[abc] 字元集合 (Character Set) 匹配集合內任一字元
[^0-9] 否定字元集合 (Negated Character Set) 匹配不在集合內的字元
\d 數字字元類 (Digit Character Class) 匹配任意數字 (0-9)
\w 單字字元類 (Word Character Class) 匹配字母、數字、底線
\s 空白字元類 (Whitespace Character Class) 匹配空格、tab、換行等空白字元
\b 單字邊界 (Word Boundary Anchor) 單字與非單字字元的邊界
^ 字串開頭錨點 (Start Anchor) 匹配字串開始位置
$ 字串結尾錨點 (End Anchor) 匹配字串結尾位置
  • 中文等 Unicode 處理依照程式語言,要注意使用 Unicode 標誌(如 JavaScript 的 /u)或相應設定,才能正確匹配。
    範例參考:Lookbehind 任意中文範例
  • . 預設不匹配換行符,需要加 s 模式或用 [\s\S]

2. 量詞(Quantifiers)

語法 說明 範例
* 0 次或多次 a* 可匹配空字串、a、aaa
+ 1 次或多次 a+ 可匹配 a、aaa
? 0 次或 1 次 a? 可匹配有或沒有 a
{n} 恰好 n 次 a{3} 可匹配 aaa
{n,} 至少 n 次 a{2,} 可匹配 aa、aaa...
{n,m} 至少 n 次,至多 m 次 a{2,4} 可匹配 aa、aaa、aaaa

3. 群組與擷取(Grouping and Capturing)

在使用純字元匹配的正則表達式時,命中的結果稱為 match (整體匹配)。當正則中使用括號 (pattern) 時,括號內匹配的部分稱為 group (群組),可以用來擷取子串或做回溯引用。

match & group 匹配的程式語言實作可以參考本篇 正則群組擷取範例

語法 說明 範例
( ... ) 捕捉群組 (Capturing Group) (ab)+ 捕捉並重複 ab
(?: ... ) 非捕捉群組 (Non-capturing Group) (?:ab)+ 只分組但不捕捉
\1, \2 回溯引用 (Backreference) <(\w+)>.*<\/\1> HTML 標籤匹配
(?P<name> ... ) 命名捕捉群組 (Named Capturing Group) Python 中 (?P<first>\w+)

4. 位置與條件判斷(Anchors and Assertions)

語法 說明 範例
^ 字串開頭錨點 (Start Anchor) ^Hello
$ 字串結尾錨點 (End Anchor) world$
\b 單字邊界 (Word Boundary) \bword\b
(?= ... ) 正向先行斷言(Lookahead) \d(?=元) 配在「元」前的數字
(?! ... ) 負向先行斷言(Negative Lookahead) \d(?!元) 不配「元」前的數字
(?<= ... ) 正向後顧斷言(Lookbehind) (?<=\$)\d+ 配在 $ 後的數字
(?<! ... ) 負向後顧斷言(Negative Lookbehind) (?<!\$)\d+ 配非 $ 開頭的數字

Lookahead 與 Lookbehind 雖然使用括號 (),和一般群組(grouping)語法相同,但它們屬於特殊的「零寬度斷言」(zero-width assertions)。這類斷言不會消耗字元,也不會將括號內的內容當作捕捉群組(capturing group),因此不會出現在 match 的群組結果中,而是僅用來判斷匹配位置是否符合特定條件。

在實務應用中,若需抓取符合某條件之後的字串,通常可以透過一般的群組語法 (pattern) 來實現,並從群組(如 group 1)中擷取目標結果;而使用 Lookahead 或 Lookbehind 則可以讓最終匹配結果(即整體 match)直接是目標字串本身,更簡潔也更具彈性。

範例比較:Grouping vs Lookbehind

Lookbehind 效能較差,且並非所有語言或正則引擎皆支援,例如 Go、Rust 等採用簡化實作的正則引擎可能無法使用。


5. 字元範圍與 Unicode 支援(Character Ranges & Unicode)

語法 說明 範例
[a-z] 小寫字母範圍 匹配 a~z
[A-Z] 大寫字母範圍 匹配 A~Z
[0-9] 數字範圍 匹配 0~9
[\u4e00-\u9fff] Unicode 中文範圍 匹配漢字範圍
\p{L}, \p{N} Unicode 字母與數字類別(需語言支援) 匹配所有字母或數字

6. 實務應用範例(Practical Examples)

需求 Regex 範例 說明
抓 Email 地址 \b[\w.-]+@[\w.-]+\.\w+\b 基本 Email 格式匹配
抓網址 https?://[^\s]+ 匹配 http 或 https 開頭網址
抓三個字的中間字 ^.(.).$ 抓三字詞中間那字
抓最後 5 個字 .{5}$ 抓字串末尾 5 個字
抓字串中特定字後的數字 (?<=價格:)\d+ 抓「價格:」後面的數字

範例

正則群組擷取範例

JavaScript

const str = '王小明';
const regex = /^.(.).$/;
const result = str.match(regex);

console.log(result[0]); // 整體 match,輸出:王小明
console.log(result[1]); // group 1,輸出:小

Python

import re

text = "王小明"
pattern = r"^.(.).$"
match = re.match(pattern, text)

if match:
    print(match.group(0))  # 整體 match,輸出:王小明
    print(match.group(1))  # group 1,輸出:小

Java

import java.util.regex.*;

public class RegexGroupExample {
    public static void main(String[] args) {
        String text = "王小明";
        Pattern pattern = Pattern.compile("^.(.).$");
        Matcher matcher = pattern.matcher(text);

        if (matcher.find()) {
            System.out.println(matcher.group(0));  // 整體 match,輸出:王小明
            System.out.println(matcher.group(1));  // group 1,輸出:小
        }
    }
}

PHP

<?php
$text = "王小明";
$pattern = "/^.(.).$/u";
if (preg_match($pattern, $text, $matches)) {
    echo $matches[0] . "\n";  // 整體 match,輸出:王小明
    echo $matches[1] . "\n";  // group 1,輸出:小
}
?>

Lookbehind 斷言範例

Python

import re

text = "王小明"
pattern = r"(?<=^.)[^\n]"  # 往前看前一字元為任意一字元(即第二個字)

match = re.search(pattern, text)
if match:
    print(match.group())  # 輸出:小(整體 match,即第二個字)

JavaScript (ES2018+ 支援)

const text = "王小明";
const regex = /(?<=^.)[^\n]/u;

const match = text.match(regex);
if (match) {
  console.log(match[0]);  // 輸出:小
}

.NET (C#)

using System;
using System.Text.RegularExpressions;

class Program {
    static void Main() {
        string text = "王小明";
        Regex regex = new Regex(@"(?<=^.)[^\n]", RegexOptions.None);
        Match match = regex.Match(text);
        if (match.Success) {
            Console.WriteLine(match.Value);  // 輸出:小
        }
    }
}

Java (Java 9+ 支援)

import java.util.regex.*;

public class LookbehindExample {
    public static void main(String[] args) {
        String text = "王小明";
        Pattern pattern = Pattern.compile("(?<=^.)[^\n]");
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            System.out.println(matcher.group());  // 輸出:小
        }
    }
}

PHP (PCRE 支援)

<?php
$text = "王小明";
$pattern = "/(?<=^.)[^\n]/u";

if (preg_match($pattern, $text, $matches)) {
    echo $matches[0] . "\n";  // 輸出:小
}
?>

Leave a Reply

Your email address will not be published. Required fields are marked *