繁體中文
English 简体中文 繁體中文
Methodox 首頁

正規表示式——全面指南

目錄

  1. 簡介
  2. 線上測試器
  3. 實用語法
  4. 全面參考
  5. 結語

1. 簡介

文字——無論是日誌檔案、配置資料、使用者輸入還是文件——在程式設計中無處不在。從簡單的指令碼到完整的應用,開發者經常需要搜尋、驗證、提取或轉換文字片段。雖然許多 IDE 和編輯器(如 Notepad++、VS Code 或 Sublime Text)提供了基本的尋找和替換功能,但當你需要處理模式、條件邏輯或重複結構時,這些功能很快就會捉襟見肘。這正是正規表示式(regex)的用武之地。

從本質上講,正規表示式是一種描述一組字串的緊湊、宣告式的方式。比如說,你不必手動逐行掃描文字來找出日誌中的所有警告訊息,而是可以編寫一個模式,匹配時間戳、日誌級別、檔名、電子郵件地址、URL——任何你能想到的內容。一旦理解了基礎知識,正規表示式就可能成為你開發者工具箱中最強大的“武器”之一。

來看一段典型的日誌:

2025-05-31 14:23:48 [Info] Starting application...
2025-05-31 14:23:50 [Info] Loading application data...
2025-05-31 14:23:55 [Warning] Missing font file: New Times Roman.
2025-05-31 14:24:22 [Info] The application has started, with 1 warning.
2025-05-31 14:25:01 [Error] Unhandled exception: NullReferenceException.

如果你想回答這樣的問題:

  • “發出了哪些警告,分別在什麼時候?”
  • “哪些行包含錯誤訊息?”
  • “應用啟動了多少次?”

你可以用自己喜歡的語言編寫一個指令碼,按空格拆分、檢查各個標記並進行字串比較。但這很快就會變得繁瑣:時間戳格式的多變、可選欄位的處理、空白字元的各種怪異情況,都可能讓你的程式碼變得脆弱。

然而,藉助一個精心編寫的正規表示式,你可以匹配整行日誌,並捕獲時間戳、日誌級別以及訊息的其餘部分——全部在一個模式中完成。更重要的是,正規表示式可以處理任何具有一致結構的基於文字的格式:CSV 檔案、HTML/XML 標籤、自訂配置格式,等等。

讀完本文後,你將:

  1. 理解正規表示式背後的基本動機。
  2. 瞭解適用於日常任務的實用語法元素和示例。
  3. 擁有一份彙總最常用正規表示式結構的便捷參考。

無論你只是在編輯器中做過一些“尋找和替換”,還是編寫過簡單的字串處理程式碼,本指南都將幫你跨越這道鴻溝,並向你展示如何藉助正規表示式更上一層樓。

2. 線上測試器

正規表示式測試器(.NET / C# 語法*)

選項 * 全域(g)始終開啟,因此會顯示所有匹配項。

替換(可選)

3. 實用語法

本節將介紹正規表示式模式的核心構建塊。每個小節先介紹一個概念,然後給出真實場景中的示例——有些基於日誌處理,有些則涉及驗證電子郵件地址或從文字中提取數字等日常任務。

3.1 字面字元與簡單匹配

在最簡單的層面上,正規表示式可以只由字面字元組成。例如:

  • 模式 Error 會匹配文字中出現的任何子字串“Error”。
  • 模式 2025-05-31 會匹配這個確切的日期字串。

然而,真實世界中的文字很少能完美對齊。例如,日誌訊息中包含不斷變化的時間戳和不同的日誌級別。要以通用的方式匹配它們,你需要用到元字元。

要點: 正規表示式中的大多數字符都按字面匹配其自身,特殊的元字元(如 .、*、?、\ 等)除外。要匹配字面意義上的句點、方括號或其他元字元,你必須用反斜槓對其進行“轉義”(用 \. 匹配字面的點,用 \[ 匹配字面的左方括號,依此類推)。

3.2 字元類([])與範圍

字元類是用方括號括起來的一組字元,它恰好匹配該組中的一個字元。例如:

  • [AEIOU] 匹配任意一個大寫母音字母。
  • [0-9] 匹配 0 到 9 之間的任意一個數字。
  • [A-Za-z0-9_] 匹配一個典型的單詞字元(字母、數字或底線)。

示例 1:匹配四位數年份

假設你想匹配 1900 到 2099 之間的任意年份:

^(19|20)[0-9]{2}$
  • ^ 和 $ 是錨點(我們稍後會介紹)。
  • (19|20) 匹配“19”或“20”。
  • [0-9]{2} 恰好匹配兩個數字。
  • 合在一起,它匹配“1900”到“2099”。

示例 2:提取日誌級別

給定這樣一行日誌:

2025-05-31 14:23:55 [Warning] Missing font file: New Times Roman.

你可以用以下模式匹配日誌級別(Info、Warning、Error 等):

\[(Info|Warning|Error|Debug)\]
  • \[ 和 \] 匹配字面的方括號。
  • (Info|Warning|Error|Debug) 匹配這四個單詞之一。

如果你預計還會有其他級別(例如 Trace 或 Fatal),可以把它們加到括號內:

\[(?:Info|Warning|Error|Debug|Trace|Fatal)\]

這裡我們使用了 (?: … ) 來建立一個非捕獲組(我們將在第 5 節中詳細討論分組)。

3.3 量詞:重複控制

量詞指定前面的元素應該匹配多少次。最常見的量詞有:

  • *——匹配零次或多次(貪婪)。
  • +——匹配一次或多次(貪婪)。
  • ?——匹配零次或一次(使前面的標記成為可選)。
  • {n}——恰好匹配 n 次。
  • {n,m}——匹配 n 到 m 次(包含兩端)。
  • {n,}——匹配 n 次或更多次。

示例 3:匹配時間戳

像 2025-05-31 14:23:48 這樣的時間戳可以用以下模式匹配:

\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}
  • \d 是 [0-9] 的簡寫。
  • \d{4} 恰好匹配四個數字(年份)。
  • - 匹配字面的連字元。
  • \d{2} 匹配兩個數字(月和日)。
  • \s+ 匹配一個或多個空白字元(空格、製表符)。
  • \d{2}:\d{2}:\d{2} 匹配 HH:MM:SS。

如果你確定日期和時間之間恰好只有一個空格,可以用 \s 代替 \s+。

示例 4:可選的副檔名

假設你有 report.txt 或 report 這樣的檔名。要同時匹配兩者,可以使用:

^report(?:\.txt)?$
  • ^ 和 $ 確保我們匹配整個字串。

  • (?:\.txt)? 的意思是“可選地匹配‘.txt’”。

    • \. 匹配字面的點。
    • txt 匹配“txt”。
    • 組後面的 ? 使其成為可選。

3.4 錨點:定位匹配

錨點指定的是文字中的位置,而不是實際的字元:

  • ^——行/字串的開頭。
  • $——行/字串的結尾。
  • \b——單詞邊界(位於 \w 和 \W 之間)。
  • \B——非單詞邊界。

示例 5:以“Error”開頭的行

如果你想找出所有以單詞“Error”(作為獨立單詞)開頭的行,可以這樣寫:

^Error\b.*$
  • ^Error 確保該行以“Error”開頭。
  • \b 斷言此處為單詞邊界,這樣“Errors”或“ErrorCode”就不會被匹配。
  • .* 匹配該行的其餘部分(任意字元,零次或多次)。
  • $ 錨定行尾。

3.5 分組與捕獲

圓括號 ( 和 ) 有兩個主要用途:

  1. 分組:將多個標記視為一個整體(通常與量詞或選擇結構一起使用)。
  2. 捕獲:儲存該組匹配到的文字,以便之後取用(在程式碼中或替換字串中)。

示例 6:捕獲時間戳和級別

給定:

2025-05-31 14:23:55 [Warning] Missing font...

你可以這樣寫:

^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+\[(Info|Warning|Error|Debug)\]\s+(.*)$
  • (\d{4}-\d{2}-\d{2}) 捕獲日期(Group 1)。

  • (\d{2}:\d{2}:\d{2}) 捕獲時間(Group 2)。

  • (Info|Warning|Error|Debug) 捕獲日誌級別(Group 3)。

  • (.*) 捕獲訊息的其餘部分(Group 4)。

  • 在大多數語言中,匹配之後你可以提取 Group 1、Group 2 等:

    • 例如在 C# 中:match.Groups[1].Value 給出日期。

如果你想分組但不捕獲(用於選擇結構或量詞),請使用 (?: … )。例如:

(?:Info|Warning|Error|Debug)

不會建立帶編號的反向引用。

3.6 常用元字元與轉義序列

一些最常用的簡寫和元字元包括:

  • .——匹配除換行符號以外的任意字元(在大多數風格中如此;有些風格允許啟用“dotall”模式來匹配換行符號)。
  • \d——數字(等價於 [0-9])。
  • \D——非數字(等價於 [^0-9])。
  • \w——單詞字元(字母、數字、底線;[A-Za-z0-9_])。
  • \W——非單詞字元(任何不屬於 \w 的字元)。
  • \s——空白字元(空格、製表符、換行符號等)。
  • \S——非空白字元。
  • \t、\n、\r——製表符、換行符號、回車符。

提示: 在程式碼中編寫正規表示式時,你通常需要對反斜槓進行轉義。例如,在 C# 中,你可以使用逐字字串字面量(@"\d{4}-\d{2}-\d{2}")來避免雙重轉義。

3.7 實用示例:提取電子郵件地址

假設你有一份文件或配置檔案,需要從中提取所有電子郵件地址。一個簡單(但並不完全符合 RFC)的模式是:

\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b
  • \b——確保從單詞邊界開始。
  • [A-Za-z0-9._%+-]+——一個或多個字母、數字、點、底線、百分號、加號或連字元(@ 之前的本地部分)。
  • @——字面的“@”符號。
  • [A-Za-z0-9.-]+——一個或多個字母、數字、點或連字元(域名)。
  • \.——字面的點。
  • [A-Za-z]{2,}——頂級域名至少包含兩個字母(例如“com”、“org”、“io”)。
  • \b——在單詞邊界處結束,以避免部分匹配(這樣就不會匹配到末尾的標點符號)。

測試一下

給定:

Please contact alice@example.com or bob.smith@my-domain.io for more info. Alternatively, reach out to admin@localhost if testing locally.
  • alice@example.com 匹配。
  • bob.smith@my-domain.io 匹配。
  • admin@localhost 不匹配,因為 localhost 沒有“. + 兩個或更多字母”形式的頂級域名。

3.8 實用示例:驗證北美電話號碼

北美編號計劃(NANP)的電話號碼通常是這樣的:

  • 123-456-7890
  • (123) 456-7890
  • 123.456.7890
  • +1 (123) 456-7890

一個實用的正規表示式可以是:

^(\+1\s?)?           # Optional country code +1 and a space
(?:\(\d{3}\)|\d{3})  # Either (123) or 123
[ .-]?               # Separator: space, dot, or hyphen (optional)
\d{3}                # Next three digits
[ .-]?               # Separator again (optional)
\d{4}$               # Last four digits

壓縮之後(去掉空白和註解):

^(\+1\s?)?(?:\(\d{3}\)|\d{3})[ .-]?\d{3}[ .-]?\d{4}$
  • ^ 和 $ 將模式錨定到整個字串。
  • (\+1\s?)?——可選地匹配“+1”,其後可以跟一個可選的空格。
  • (?:\(\d{3}\)|\d{3})——括號內的三個數字,或者不帶括號的三個數字。
  • [ .-]?——可選地匹配一個空格、點或連字元。
  • \d{3}——三個數字。
  • [ .-]?——再次匹配分隔符。
  • \d{4}——四個數字。

這個模式可以匹配大多數常見的北美號碼格式。通過調整分組和量詞,你可以使其適用於其他地區,或採用更嚴格/更寬鬆的規則。

3.9 實用示例:解析日誌檔案

回到我們的日誌示例,假設你只想提取警告訊息及其時間戳。你可以使用這樣的正規表示式:

^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$
  • Group 1 捕獲時間戳(2025-05-31 14:23:55)。
  • Group 2 捕獲訊息本身(Missing font file: New Times Roman.)。

在大多數程式語言中:

  • C#(使用 System.Text.RegularExpressions):

    var pattern = @"^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$";
    var matches = Regex.Matches(logContents, pattern, RegexOptions.Multiline);
    foreach (Match m in matches) {
        string timestamp = m.Groups[1].Value;
        string message   = m.Groups[2].Value;
        Console.WriteLine($"[{timestamp}] Warning: {message}");
    }
    
  • Python(使用 re 模組):

    import re
    
    pattern = r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$'
    for line in log_contents.splitlines():
        m = re.match(pattern, line)
        if m:
            timestamp = m.group(1)
            message   = m.group(2)
            print(f"[{timestamp}] Warning: {message}")
    

即使你只在編輯器中按一下過“Replace”(替換),這些示例也應該能讓你清楚地感受到一個正規表示式有多麼強大。你定義了“匹配直到 [Warning] 為止的所有內容,然後捕獲其後的任何內容”,就能準確得到你想要的資料。

3.10 測試與除錯正規表示式

剛接觸正規表示式時,很容易忽略一些小錯誤——漏掉轉義、放錯量詞的位置、忘記錨點。下面是一些建議:

  1. 使用線上測試器 regex101.com(PCRE、JavaScript、Python)、RegexStorm(適用於 .NET)或 regexr.com 等網站可以讓你輸入模式、貼上測試文字,並即時檢視匹配結果和高亮顯示。

  2. 從簡單開始,逐步擴充 如果你需要一個複雜的模式,先從一小部分開始。例如,先只用 \d{4}-\d{2}-\d{2} 測試日期,然後加上時間,再加上日誌級別,依此類推。

  3. 為模式新增註解(在支援的情況下) 一些語言/風格支援“擴充”或“自由間距”模式,在這種模式下,你可以在正規表示式中新增空白和 # comments:

    (?x)            # Enable free-spacing mode
    ^               # Start of line
    (\d{4}-\d{2}-\d{2})   # Group 1: Date
    \s+             # One or more spaces
    (\d{2}:\d{2}:\d{2})   # Group 2: Time
    \s+             # One or more spaces
    \[Warning\]     # Literal [Warning]
    \s+             # One or more spaces
    (.*)            # Group 3: Message text
    $
    

    這讓維護和日後的修改更加容易,對於複雜的正規表示式尤其如此。

  4. 儘早轉義 如果你需要匹配特殊字元(.、*、?、+、[、]、(、)、|、^、$、\),除非你明確需要它們的特殊行為,否則一律用反斜槓對其進行轉義。

4. 全面參考

下面彙總了最常用的正規表示式結構。可以把它當作一份快速參考的“速查表”。

注意: 不同的正規表示式風格(PCRE、Python、JavaScript、.NET、Java 等)在一些細節上有所不同,但下面的大部分條目都是通用的。存在特定於風格的說明時,會加以註明。

4.1 基本元字元

元字元 含義
. 匹配除換行符號以外的任意字元(除非啟用了“dotall”/“singleline”模式)。
^ 匹配行/字串的開頭。
$ 匹配行/字串的結尾。
\b 單詞邊界(\w 與 \W 之間的過渡處)。
\B 非單詞邊界(與 \b 相反)。
\d 數字,等價於 [0-9]。
\D 非數字,等價於 [^0-9]。
\w 單詞字元,等價於 [A-Za-z0-9_]。
\W 非單詞字元,等價於 [^A-Za-z0-9_]。
\s 空白字元(空格、製表符、換行符號、垂直製表符、換頁符等)。
\S 非空白字元(任何不被 \s 匹配的字元)。
\t 製表符。
\n 換行符號(LF)。
\r 回車符(CR)。
\f 換頁符。
\v(特定於風格) 垂直製表符(部分風格支援)。
\\ 字面的反斜槓。
\. 字面的點。
\* 字面的星號。
\+ 字面的加號。
\? 字面的問號。
\{ \} 字面的花括號。
\[ \] 字面的方括號。
\( \) 字面的圓括號。
\| 字面的豎線/選擇運算子。
\^ 字面的脫字元(字串開頭錨點)。
\$ 字面的美元符號(字串結尾錨點)。

4.2 量詞

量詞 含義
* 匹配前面的標記零次或多次(貪婪)。
+ 匹配前面的標記一次或多次(貪婪)。
? 匹配前面的標記零次或一次(使其成為可選;也用於惰性量詞)。
{n} 匹配前面的標記恰好 n 次。
{n,} 匹配前面的標記n 次或更多次(無上限)。
{n,m} 匹配前面的標記至少 n 次但不超過 m 次。
*?、+?、??、{n,m}? 上述量詞的“惰性”或“非貪婪”版本(匹配儘可能少的字元)。

貪婪與惰性:

  • 貪婪量詞(*、+、{n,m})會在仍能讓模式其餘部分匹配成功的前提下,嘗試匹配儘可能多的文字。
  • 惰性量詞(*?、+?、{n,m}?)會匹配儘可能少的文字,只要整體匹配能夠成功即可。

示例:貪婪與惰性

  • 模式 a.*b 作用於文字 axxxbxxb 時,會匹配 axxxbxxb(貪婪:從第一個 a 到最後一個 b)。
  • 模式 a.*?b 會匹配 axxxb(惰性:從 a 到第一個 b)。

4.3 字元類與字符集

語法 匹配
[abc] 字元 a、b 或 c 中的任意一個。
[^abc] 除 a、b 或 c 之外的任意字元。
[a-z] 從 a 到 z 的任意小寫字母。
[A-Z] 從 A 到 Z 的任意大寫字母。
[0-9] 從 0 到 9 的任意數字。
[A-Za-z0-9_] 任意“單詞”字元(通常等價於 \w)。
[[:alnum:]](POSIX) 字母和數字(POSIX 風格)。可在部分風格中使用(例如 GNU、支援 [[:alnum:]] 的 PCRE)。
[[:space:]]、[[:digit:]] 等 POSIX 字元類(各風格的支援情況不一)。

示例:匹配十六進位制數字

^[0-9A-Fa-f]+$
  • 匹配任何完全由十六進位制數字組成的非空字串。

4.4 選擇(|)與分組

  • 分組:( … )

    • 捕獲組:儲存匹配到的文字,以供反向引用或提取。
    • 非捕獲組:(?: … ) 不儲存匹配內容;適用於只需分組而無需捕獲的場合。
  • 選擇:A|B 匹配模式 A 或模式 B。

示例:匹配多種副檔名

^.*\.(?:jpg|jpeg|png|gif|bmp)$
  • .*\. 匹配直到點(含點)為止的任意檔名部分。
  • (?:jpg|jpeg|png|gif|bmp) 匹配這五種副檔名之一。
  • $ 確保副檔名位於末尾。

4.5 錨點與邊界

錨點 含義
^ 字串開頭(在多行模式下為行首)。
$ 字串結尾(在多行模式下為行尾)。
\A 字串開頭(不受多行模式影響)。
\z 或 \Z 字串結尾(\z 是絕對結尾;\Z 允許末尾有一個可選的換行符號)。
\b 單詞邊界(位於 \w 和 \W 之間)。
\B 非單詞邊界(與 \b 相反)。

多行模式(m 標誌)

  • 如果啟用多行模式(通常通過一個標誌,例如 .NET 中的 RegexOptions.Multiline 或 Python 中的 re.MULTILINE),^ 和 $ 將匹配每一行的開頭/結尾,而不是整個字串的開頭/結尾。

4.6 環視(零寬斷言)

環視讓你可以斷言某個匹配之前或之後是(或不是)某些內容,而不會把這些“內容”包含在匹配結果中。

結構 含義
(?= … ) 正向先行斷言:其後的內容必須匹配 …。
(?! … ) 負向先行斷言:其後的內容必須不匹配 …。
(?<= … ) 正向後行斷言:其前的內容必須匹配 …。
(?<! … ) 負向後行斷言:其前的內容必須不匹配 …。

示例:匹配不含數字的密碼

要匹配一個長度為 6–12 個字元且不包含任何數字的密碼:

^(?=.{6,12}$)(?!.*\d).+$
  • (?=.{6,12}$)——確保長度在 6 到 12 之間。
  • (?!.*\d)——確保任何位置都沒有數字(.*\d 的意思是“某處有一個數字”;(?! … ) 表示“字串的其餘部分不得匹配 .*\d”)。
  • .+$——最後,匹配一個或多個(任意)字元,直到結尾。

4.7 反向引用

捕獲一個組之後,你可以在模式的後面部分引用它(以強制要求文字的兩個部分完全相同),也可以在替換字串中引用它。

  • \1、\2……引用 Group 1、Group 2 等匹配到的文字(在大多數風格中如此)。
  • 在替換字串中(例如在 .NET 或 JavaScript 中),你可能會使用 $1、$2 等。

示例:匹配 HTML/XML 的開始標籤和結束標籤

要確保開始標籤 <tag> 由同名的 </tag> 閉合:

^<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>$
  • <([A-Za-z][A-Za-z0-9]*)\b[^>]*>

    • 在 Group 1 中捕獲標籤名(例如 div、span、p)。
    • \b 確保標籤名之後是一個單詞邊界。
    • [^>]* 匹配開始標籤中的任何屬性。
  • (.*?)

    • 惰性地捕獲元素內的任何內容(Group 2)。
  • </\1>

    • 匹配與 Group 1 中捕獲的標籤名完全相同的結束標籤(例如 </div>)。

這個簡單的模式可以匹配像 <p class="intro">Hello</p> 這樣的完整元素,但它無法處理同名標籤的巢狀元素。對於巢狀結構,你需要更高階的解析方法(通常超出了基本正規表示式所能穩健處理的範圍)。

4.8 常用簡寫與轉義序列

序列 含義
\d 數字(與 [0-9] 相同)。
\D 非數字(與 [^0-9] 相同)。
\w 單詞字元 [A-Za-z0-9_]。
\W 非單詞字元 [^A-Za-z0-9_]。
\s 空白字元 [ \t\r\n\f\v]。
\S 非空白字元。
\t、\n、\r、\f 製表符、換行符號、回車符、換頁符。
\0 空字元(U+0000)。
\xhh 十六進位制程式碼為 hh(兩位十六進位制數字)的字元。
\uhhhh 程式碼為 hhhh(四位十六進位制數字)的 Unicode 字元。(特定於風格:.NET、Java、帶 u 標誌的 JavaScript 等。)
\cX 控制字元(例如 \cA 為 U+0001)。
\Q … \E 在某些風格(如 .NET)中,\Q 開始一個引用區段(直到 \E 為止的所有內容都按字面處理)。

4.9 佔有量詞(量詞後加 +)與原子組(特定於風格)

一些正規表示式風格(如 Java、PCRE)支援佔有量詞(*+、++、?+、{n,m}+),它們從不回溯:

  • 示例:a*+b

    • a*+ 匹配儘可能多的 a,並且不允許通過回溯讓 b 得以匹配。

    • 如果輸入是“aaaaabc”:

      • a*+ 匹配“aaaaa”。
      • 然後引擎嘗試匹配 b。由於下一個字元是 b,整體匹配結果為“aaaaab”。

原子組 (?> … ) 有類似的效果:一旦 (?> … ) 內的子模式匹配成功,引擎就不會再回溯到其中。

注意: 並非所有風格都支援佔有量詞或原子組。如果你需要這些高階功能,請查閱你所用語言/執行時的文件。

4.10 標誌(修飾符)與模式

大多數正規表示式引擎允許你通過標誌啟用或禁用某些行為。常見的標誌包括:

標誌 含義 典型語法
i 不區分大小寫的匹配(.NET 中為 RegexOptions.IgnoreCase)。 內聯:(?i)pattern。
m 多行模式:^ 和 $ 匹配各行的開頭/結尾,而不僅是字串的開頭/結尾。 在 .NET 中:RegexOptions.Multiline 或 (?m)pattern。
s 單行模式或 dot-all 模式:. 也匹配換行符號。 在 .NET 中:RegexOptions.Singleline 或 (?s)pattern。
x 自由間距/註解模式:忽略空白,並允許使用 # comments。 在 .NET 中:RegexOptions.IgnorePatternWhitespace 或 (?x)pattern。
U 非貪婪模式(PCRE):互換貪婪與惰性行為(很少使用)。 在 PCRE 中:(?U)pattern。
u Unicode 模式(特定於風格)。 在 JavaScript 中使用 /u 標誌,或在 .NET 中使用 RegexOptions.CultureInvariant。

示例:內聯標誌

(?im)^[a-z]+\d+\s+$
  • (?i) 表示“不區分大小寫”。
  • (?m) 表示“多行”。
  • 如果沒有標誌,^[a-z]+ 只會匹配小寫字母,並且只在整個字串的開頭匹配;有了 i 和 m,它會在每一行上匹配小寫或大寫字母。

4.11 替換模式

當你使用正規表示式執行替換時,通常會在替換字串中使用反向引用:

  • 在許多語言中(例如 JavaScript、Python),使用 $1、$2……
  • 在 .NET 中,通常同樣使用 $1、$2。
  • 在一些較舊的工具或風格中,可能會使用 \1、\2。

示例:把“Last, First”調換為“First Last”

原始文字:

Smith, John
Doe, Jane

正規表示式尋找模式:

^([A-Za-z]+),\s+([A-Za-z]+)$

替換模式:

$2 $1

替換之後:

John Smith
Jane Doe
  • ([A-Za-z]+) 在 Group 1 中捕獲姓氏。
  • ([A-Za-z]+) 在 Group 2 中捕獲名字。
  • $2 $1 在輸出中交換兩者的位置。

5. 結語

到現在,你應該已經紮實地掌握了:

  • 正規表示式為什麼對文字處理極為寶貴。
  • 如何編寫基本模式:字面字元、字元類、量詞、錨點。
  • 如何使用分組、環視和反向引用進行更高階的匹配。
  • 哪些標記和結構構成了大多數實用正規表示式的“核心”。
  • 需要溫習時去哪裡尋找(上面的參考部分,或 regexstorm.net/reference 這樣的網站)。

只需幾十分鐘的練習——針對示例文字編寫正規表示式——你就能迅速建立信心。請記住:

  1. 儘早測試、經常測試:在線上測試器或 IDE 的“Find in Files”(在檔案中尋找)中測試——即時看到匹配結果,遠比手動肉眼檢查程式碼更有啟發。
  2. 可讀性很重要。 如果模式變得難以閱讀,請使用自由間距模式((?x))並新增註解。
  3. 對於常見任務,保持簡單。你很少需要最晦澀的語法;匹配日誌級別、日期、電子郵件地址或基本的 CSV 欄位,通常用簡單直接的字元類和量詞就能完成。

掌握基礎之後,你可以探索更高階的主題,例如:

  • 原子分組和佔有量詞(用於對效能要求很高的模式)。
  • 遞迴模式(在 PCRE 或一些高階風格中),用於匹配巢狀結構。
  • 條件子模式(在 PCRE 或 .NET 中),在決定模式的下一部分之前,先測試某個組是否已經匹配。

不過,這些主題就留待以後再談。現在,請藉助上面的示例和參考表,在你的下一個文字處理挑戰中嘗試運用正規表示式。用不了多久,你就會發現自己開始用模式來思考——而且再也回不去了。

恭喜你踏上了打造模式的旅程!把這份指南放在手邊,收藏你最喜歡的正規表示式測試器,並記住:一個寫得好的正規表示式,往往能把數小時的手動解析變成一行優雅的程式碼。

參考資料