【Regex】解析非捕獲分組與斷言語法

【Regex】解析非捕獲分組與斷言語法

本篇重點

  • 說明擴充型分組的用途
  • 說明非捕獲分組 ?: 的用途
  • 說明先行斷言 ?=?!、後行斷言 ?<=?<! 的用途與差異
  • 非捕獲分組有匹配但不記錄分組結果
  • 斷言只檢查條件不匹配任何內容

擴充型分組

(? 開頭,屬於正規表達式的「擴充型分組(Extended Group)」語法,主要用途是改變群組行為設定匹配條件

類型語法名稱是否匹配字元是否捕獲內容
一般分組(pattern)capturing group
非捕獲分組(?:pattern)Non-capturing group
斷言(先行/後行)(?=...)(?!...)(?<=...)(?<!...)Lookaround assertion

非捕獲分組

非捕獲分組(Non-capturing Group)是讓正規式結構化重複某段規則,但不捕獲分組結果,仍然會參與實際的匹配過程,只是不會佔用群組索引或儲存結果。

使用時機

(?:pattern)

  • 建立條件組合但不希望影響 $1$2 群組索引。

  • 群組僅為結構性用途(如限定重複次數或選擇):

    1. 限制重複次數

      你需要讓一段模式重複多次時,必須用括號把那段包起來:

      js
      1
      2
      const regex = /(?:ab){2}/;
      console.log("abab".match(regex)); // ["abab"]

      這裡 (ab){2} 表示「ab 重複兩次」,若不用括號 /ab{2}/ 則表示「a 後面接兩個 b」→ “abb”,使用 (?:ab){2} 而不是 (ab){2} 是因為不需要捕獲 “ab”,只是想讓整段重複。

    2. 選擇(alternation)用途

      當你用 |(或條件)時,常需要把多個選項包起來:

      js
      1
      2
      3
      const regex = /^(?:cat|dog)s?$/;
      console.log("cats".match(regex)); // ["cats"]
      console.log("dog".match(regex)); // ["dog"]

      (?:cat|dog) 表示「cat 或 dog」,s? 表示可選的複數 s,這樣寫是為了讓正則語法正確運作,並且不需捕獲 “cat” 或 “dog”。

範例

js
1
2
3
4
5
const str = "2025-11-05, 2024-07-20";
const regex = /(?:\d{4})-(\d{2})-(\d{2})/;
const match = str.match(regex);

console.log(match); // ["2025-11-05", "11", "05"]

說明:

年份以 (?:...) 包起來,只是為了表達結構,不需要在結果中取出,因此不建立捕獲群組,但有匹配字元。

斷言

斷言(Assertions, Lookaround)屬於零寬度匹配(zero-width match),也就是只檢查字串中「某個位置的前後條件」,不消耗字元、也不會出現在結果中

斷言可分為四種:

類型語法檢查方向條件
正向先行斷言(?=pattern)後方必須符合
負向先行斷言(?!pattern)後方不得符合
正向後行斷言(?<=pattern)前方必須符合
負向後行斷言(?<!pattern)前方不得符合

正向先行斷言

(?=pattern) 檢查某位置後方是否符合指定條件。

若符合,該位置可被匹配,但 pattern 本身不會納入結果。

範例

js
1
2
3
const str = "商品A 100元, 商品B 200元, 商品C 300日圓";
const regex = /\d+(?=元)/g;
console.log(str.match(regex)); // ["100", "200"]

說明

匹配「後面接著『元』」的數字,排除「日圓」的數字。

負向先行斷言

(?!pattern) 檢查某位置後方不得符合指定條件。

範例

js
1
2
3
const str = "apple pie, apple tart, apple juice";
const regex = /apple(?!\start)/g;
console.log(str.match(regex)); // ["apple", "apple"]

說明

  • /apple(?!\start)/g\s 是空白的意思。
  • 僅匹配後方不是 “ tart” 的 “apple”。

正向後行斷言

(?<=pattern) 檢查某位置前方是否符合指定條件,若符合,即可從該位置開始匹配。

範例

js
1
2
3
const str = "JPY300, USD120, USD500";
const regex = /(?<=USD)\d+/g;
console.log(str.match(regex)); // ["120", "500"]

說明

只取出「前方是 USD」的數字。

負向後行斷言

(?<!pattern) 檢查某位置前方不得符合指定條件。

範例

js
1
2
3
const str = "VIP123, GUEST456, USER789";
const regex = /(?<!VIP)\d+/g;
console.log(str.match(regex)); // ["23", "456", "789"]

說明

斷言只檢查匹配開頭的位置,並不會讓整段文字「整體排除」,因此會在 "VIP123" 的第 4 個位置(2)發現條件成立(前方不等於 VIP),開始匹配數字,得到 "23"

非捕獲分組與斷言的差異

比較項目非捕獲分組 (?:pattern)斷言 ( ?= / ?! / ?<= / ?<! )
是否參與匹配✅ 有實際匹配字元❌ 不匹配字元(零寬度)
是否捕獲內容❌ 不捕獲❌ 不捕獲
主要功能結構化表達式、不記錄群組檢查前後條件
實際應用範例建立非捕獲群組以控制重複或選擇根據上下文條件限制匹配範圍

範例

js
1
2
3
4
5
6
7
const str = "Price: $100, €200, $300";

// 非捕獲分組
console.log(str.match(/(?:\$)\d+/g)); // ['$100', '$300']

// 斷言
console.log(str.match(/(?<=\$)\d+/g)); // ['100', '300']
  • 非捕獲分組 → 有匹配、但不記錄分組結果。
  • 斷言 → 只檢查條件、不匹配任何內容。

結論

非捕獲分組與四種斷言語法是讓正規表達式更具彈性的重要工具:

  • (?:pattern):非捕獲分組,用於結構化匹配但不取值。
  • (?=pattern)(?!pattern):先行斷言,用於判斷後方條件。
  • (?<=pattern)(?<!pattern):後行斷言,用於判斷前方條件。

延伸閱讀

作者

健忘工程師

發表於

2025-11-12

更新於

2025-11-12

許可協議


你可能也想看

【PHP、JavaScript】陣列自定義排序
【PHP、JavaScript】三種實用的符號運算子 ?:、??、??=
【Tool】cron 表達式(排程時間)轉換

評論

複製完成