
正規表示式的前瞻與後顧:JavaScript lookahead、lookbehind 實用範例
前瞻(lookahead)與後顧(lookbehind)都是零寬度斷言:它們檢查目前位置前後的文字是否符合條件,卻不把檢查的文字消耗進整體匹配。這不代表內部不能擷取;一般括號仍可建立擷取群組,不需要擷取時可使用 (?:...)。以下以 JavaScript 範例整理四種形式與實務限制。
日文原文發布: 2026-04-17
四種環視斷言
表格若超出畫面寬度,可左右捲動。
| 語法 | 名稱 | 條件 |
|---|---|---|
(?=X) | 正向前瞻 | 目前位置後方符合 X |
(?!X) | 負向前瞻 | 目前位置後方不符合 X |
(?<=X) | 正向後顧 | 目前位置前方符合 X |
(?<!X) | 負向後顧 | 目前位置前方不符合 X |
前瞻與後顧統稱環視(lookaround)。它們不消耗字元,也不推進整體匹配的位置,因此可在同一位置接連測試多個條件,達成類似 AND 的效果。
範例一:只擷取指定後綴前的數字
假設輸入格式已知,字串包含多個數字,但只想取出後方緊接空白與 TWD 的金額:
const text = "商品 A 500 TWD,運費 300 TWD,代碼 1234";
text.match(/\d+(?= TWD)/g);
// ["500", "300"];1234 不符合條件
也可以使用 (\d+) TWD,但整體匹配會包含後綴,之後還要讀取第 1 個擷取群組。正向前瞻則讓整體匹配只包含數字。
範例二:擷取指定前綴後的文字
所需文字前方有固定標記時,可使用正向後顧:
// 從已知格式的電子郵件字串清單擷取網域
"alice@example.com, bob@example.org".match(/(?<=@)[\w.-]+/g);
// ["example.com", "example.org"]
// 擷取錢字號後的數字文字
"價格 $99.99,稅額 $9.50".match(/(?<=\$)\d+(?:\.\d+)?/g);
// ["99.99", "9.50"]
這些只是在已知格式中擷取片段,不是完整的電子郵件位址或貨幣格式驗證器。錢字號範例也不推定幣別;若要驗證使用者輸入,必須另外定義完整可接受格式。
範例三:用負向前瞻排除後方條件
如果某種模式緊接在候選結果後方,就不接受該結果,可用負向前瞻:
// 數字串後方不能接單字字元
"abc123xyz 789".match(/\d+(?!\w)/g);
// ["789"]
// 123 後面是 x,因此不符合。
// 789 後面是字串結尾,因此符合。
有時把同一個邊界需求寫成正向條件會更清楚:
"abc123xyz789!end 456".match(/\d+(?=[^\w]|$)/g);
// ["789", "456"]
789 後面是 !,456 後面是字串結尾。此處使用未加 i、u 的範例,\w 表示 ASCII 英文字母、數字與底線,並不涵蓋中文字。負向條件不易理解時,先逐一測試邊界,再組合規則。
範例四:結合多個條件
以下同時要求:只使用 ASCII 英文字母與數字、至少各有一個,而且總長度至少 8 個字元。這類寫法常被當成密碼規則,但依 NIST SP 800-63B-4 的規定,驗證端不得要求密碼混合特定字元類別。請把它視為前瞻語法示範,不是完整的密碼政策。
const exampleRe = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
exampleRe.test("abc12345"); // true
exampleRe.test("abcdefgh"); // false:沒有數字
exampleRe.test("12345678"); // false:沒有英文字母
exampleRe.test("abc123"); // false:長度不足
兩個前瞻不推進位置,只檢查條件;最後的字元類別才消耗匹配文字。此例只檢查字元類別與長度,不會衡量密碼強度,也不會判斷是否為已外洩密碼。實際認證設計還須允許足夠的最大長度,並分別處理嘗試頻率限制、多因素驗證與已外洩密碼比對。
範例五:插入千分位逗號
面向使用者的數字格式,通常應使用 Intl.NumberFormat 或 toLocaleString()。但對已知只含 ASCII 數字的整數字串,可以用前瞻示範插入逗號:
"1234567890".replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
// "1,234,567,890"
\B 要求目前位置不是單字邊界;前瞻則要求後方有一組以上的三位數字,且再後方不能接數字。內部的 (?:...) 不建立擷取群組,因此可在由右往左每三位數的位置插入逗號。
這個例子針對只含 ASCII 數字的正整數字串。正負號、小數、指數記法與不同語系的分組方式,應交給 Intl.NumberFormat 或專門的解析器。
瀏覽器與 Node.js 相容性
前瞻
JavaScript 引擎支援前瞻已有很長時間,但仍應測試實際支援環境,尤其是內嵌或舊版 WebView,不能直接假設所有環境都沒有問題。
後顧
後顧較晚加入。現行主要瀏覽器與現行 Node.js 已支援,但舊版 Safari、iOS Safari、WebView 或 Internet Explorer 可能不支援。應以產品的支援範圍與實際使用情形決定是否採用。
// 後顧
/(?<=@)[\w.-]+/
// 改用擷取群組
const match = "alice@example.com".match(/@([\w.-]+)/);
match?.[1]; // "example.com"
這段替代寫法只處理後顧語法;若要支援更舊的 JavaScript,const 與 ?. 等語法也須另外確認或轉譯。
效能與回溯:零寬度不等於零成本
不論位於斷言內外,含糊的替代分支與 (a+)+ 等巢狀量詞,在部分輸入下都可能造成指數級回溯。零寬度只表示不消耗匹配字元;如果內部模式從許多位置反覆嘗試,仍可能做大量運算。
- 檢查共享前綴的替代分支,以及可匹配空字串的重複量詞。
- 避免在缺乏明確邊界時,反覆評估斷言中的
.*。 - 用長度很大但不匹配的輸入,以及外部可控制的輸入,檢查時間與記憶體成本。
固定長度或只有少量前瞻,都不是跨引擎的效能保證。應在正式環境相同的 JavaScript 引擎與旗標下量測;如果處理結構化語言時只能靠正規表示式近似其文法,專用解析器通常更合適。
重點整理
- 前瞻與後顧檢查位置,不消耗所檢查的文字。
- 環視內的一般群組仍可擷取;不需要擷取時使用
(?:...)。 - 多個正向前瞻可組成類似 AND 的條件,但字元類別與長度不等於密碼強度。
- 現行主要 JavaScript 環境支援後顧,舊環境可能需要擷取群組替代方案。
- 要測試正常輸入,也要在正式引擎中測試長而不匹配的輸入。
參考資料與來源
編輯說明
本文使用 AI 協助編輯,並於發布前由編輯確認。內容仍可能包含事實、解讀或時效上的錯誤;進行重要判斷前,請查閱所列的一手資料或官方文件。

