跳到主要內容

← 返回部落格

正規表達式入門:實用速查表

正規表達式在你看懂各部件之前像亂碼,一旦通了,它就是你最好用的工具之一。這是一份新手也記得住的速查表。

一次真實執行,連群組一起。 我們在正規表達式測試器裡,用 \b(\d{4})-(\d{2})-(\d{2})\b 加上 g 旗標去比對 due 2026-07-27, shipped 2026-08-03。它回報 2 match(es),並列出每一個以及它被找到的位置 —— #1 "2026-07-27" @4#2 "2026-08-03" @24。一個畫面兩個重點:沒有 g 旗標你只會看到第一個;而那三組括號正是讓你能分別取出年、月、日的原因,不必事後再去剖析比對到的字串。

字元類別

  • . —— 任意字元(換行除外,除非設 s 旗標)。
  • \d —— 數字;\D —— 非數字。
  • \w —— 單字字元(字母、數字、底線);\W —— 相反。
  • \s —— 空白;\S —— 非空白。
  • [abc] —— a、b、c 其中之一。[a-z] —— 範圍。[^abc] —— 除了 a、b、c 以外。

量詞(數量)

  • * —— 零或多個。+ —— 一或多個。? —— 零或一個(可選)。
  • {3} —— 剛好 3 個。{2,4} —— 2 到 4 個。{2,} —— 2 個以上。
  • 在量詞後加 ? 變成惰性(盡量少配):.+?

貪婪與非貪婪是量詞裡最花時間的細節,而它只需要一次量測。對 <b>bold</b> and <i>it</i>,pattern <.+> 回傳 1 個比對 —— 它從第一個 < 一路吞到最後一個 >。加一個 ?,<.+?> 回傳 4 個比對:#1"<b>"@0 #2"</b>"@7 #3"<i>"@16 #4"</i>"@21。那個 ? 不改變「什麼能被比對」,只改變 .+ 在停下來之前伸多遠。

錨點與邊界

  • ^ —— 字串開頭(設 m 時為每行開頭)。$ —— 結尾。
  • \b —— 單字邊界 —— 很適合整字比對,如 \bcat\b

偏移量才是把「比對到幾個」變成「診斷」的東西。我們的 regex 測試器會列出每一個命中以及它從哪裡開始,所以 \d+Order 12, item 345, qty 7. 得到 3 個比對,內容是

#1"12"@6 #2"345"@15 #3"7"@24

要讀數字,不只讀字串:第三個命中從 24 開始,這告訴你最後那個 7 是在句號之前被找到的,因此 . 不屬於 \d。加一個邊界就整組都變了 —— \b\w{5}\b 只回傳

#1"Order"@0

一個命中、偏移 0,因為 Order 是唯一一個獨立成詞的五字元單字。

群組與交替

  • ( … ) —— 擷取群組;比對到的文字會記成第 1、2⋯ 群組。
  • (?: … ) —— 擷取的群組(只用於結構)。
  • a|b —— 配 a b。

旗標

  • g —— 找出所有比對,而非只有第一個。
  • i —— 忽略大小寫。
  • m —— ^$ 依每行比對。
  • s —— 讓 . 也配換行。

幾個實用範式

  • 只有數字:^\d+$
  • 簡單日期:\d{4}-\d{2}-\d{2}
  • 單字:\b\w+\b
  • 粗略的 email:[^@\s]+@[^@\s]+\.[^@\s]+

(真正的 email 驗證出了名地難 —— 這種粗略樣式用於快速檢查沒問題,但不適合做到完全符合 RFC。)

四個 pattern、同一段沒變過的測試字串(Order 12, item 345, qty 7.),丟進我們自己的 regex 測試器。比對數就是整堂課的重點:決定你拿回多少的是 pattern,不是那段文字。

Pattern比對到的數量
\d+3 個比對
\d{2,}2 個比對
\b\w{5}\b1 個比對
[A-Z]\w+1 個比對

省下大把時間的技巧

  • 逐步建構。 先寫一小段再加上去,邊看比對即時更新。
  • 跳脫特殊字元,若你要它的字面意義:\.\?\(
  • 小心貪婪的 .* —— 它會盡量抓;過頭時改用 .*? 或更精確的類別。
  • 用真實資料測試,包括你預期會失敗的邊界情況。

學最快的方法是邊打邊看比對反白。在免費的正規表達式測試器即時試 —— 它會反白每個比對並列出擷取群組,全程在你的瀏覽器。

進階一點:環顧與具名群組

基礎通了之後,有兩組功能能解鎖很多用法:

  • 正向環顧(lookahead) (?=…)負向環顧 (?!…):只有在「後面接的東西」符合(或不符合)時才比對這個位置,而且不會把它吃掉。\d(?=px) 會比對「後面接著 px」的數字。
  • 後顧(lookbehind) (?<=…)(?<!…):對「前面的東西」做同樣的事。如今支援度很廣(JavaScript 自 ES2018 起,PCRE、.NET 也支援)。
  • 具名群組 (?<year>\d{4}):比起數 \1\2,可讀性高得多。可用 \k<year> 或在取代字串裡以名稱引用。

常見錯誤

  • 中括號 […] 內,特殊字元會失效。 在字元類別裡,.*( 都是字面意義;只有 ^(在開頭)、-(在字元之間)、]\ 仍有特殊作用。所以 [.?] 就是比對一個句點或問號。
  • \d 不一定是 Unicode。 在 JavaScript,\d 就等於 [0-9];有些引擎會把它擴及其他語系的數字。想要純 ASCII,就寫 [0-9]
  • 災難性回溯。 對重疊的文字用巢狀量詞——例如用 (a+)+$ 去比對一長串不符合的字串——可能讓引擎卡死。改寫掉這種模稜兩可。

最花時間的錯,是以為量詞會停在「你會停下來讀」的地方。對 <b>bold</b> and <i>it</i>,非貪婪的 <.+?> 回傳

#1"<b>"@0 #2"</b>"@7 #3"<i>"@16 #4"</i>"@21

四個命中,分別在 0、7、16、21 —— 每一個標籤,而中間的內容一個都沒有。貪婪的 <.+> 回傳一個命中、在 0,涵蓋整個字串。同樣的字元、同樣的輸入,唯一的差別是一個 ?

繼續學下去

值得「解密」的精簡語法不只正則——如果你會排定工作,那種「乍看像亂碼、通了就懂」的感覺,在 Cron 表達式上一模一樣。