Regex 深入解析:capture groups、lookaround、災難性回溯與跨語言差異
regex 是 QA / SRE / 後端工程師每天用、但永遠覺得自己不夠熟的東西。這篇整理我這幾年遇過真正會用到的進階技巧,以及那些「為什麼我的 regex 把瀏覽器卡死」的真實案例。
Capture group vs non-capturing vs named
三種寫法都在用括號,但意義不同:
- () Capture group:抓出來,可以用 $1、$2 引用
- (?:) Non-capturing group:只分組,不抓 — 效能比 () 略好,複雜 regex 應該大量用
- (? Named group:抓出來且能用名字引用 — 可讀性大勝,維護時不會數錯位置
例:抓 email
- 笨寫法:(\w+)@(\w+)\.(\w+),後續用 $1 $2 $3 — 一個月後回來看完全不知道哪個是什麼。
- 好寫法:(?,用 groups.user 直接懂。
Lookahead / lookbehind:不消耗字元的條件
Lookaround 在 regex 引擎內查條件但不前進游標,適合需要「同時滿足多條件」的場景:
- (?=...) Positive lookahead:後面必須是某東西
- (?!...) Negative lookahead:後面不能是某東西
- (?<=...) Positive lookbehind:前面必須是某東西
- (? Negative lookbehind:前面不能是某東西
實用例:抓密碼但要求至少一個數字 + 一個大寫字母
``
^(?=.*\d)(?=.*[A-Z]).{8,}$
``
三個 lookahead 並聯,沒有任何字元被「消耗」,只是檢查條件。比寫多條正規表達式組合判斷簡潔太多。
災難性回溯:會把瀏覽器卡死的寫法
Nested quantifier(巢狀量詞)幾乎一定踩雷。經典 anti-pattern:(a+)+ (a*)* (a|a)*
以 (a+)+b 配 aaaaaaaaaaaaaaaaa 為例:regex 引擎要試所有「a 怎麼分組」的排列組合才放棄,複雜度 O(2^n)。輸入只要再長一點點,瀏覽器就凍住(我自己中過 — 用 30 字元測試字串跑了 8 秒)。
怎麼避免:
1. 用 atomic group (?>...)(JS 不支援,Node ≥ 16 才支援,Java / .NET 支援)
2. 用 possessive quantifier ++ *+(同樣 JS 不支援)
3. 檢查你的 quantifier 沒有重疊((\w+)+ → 直接用 \w+)
4. 限制輸入長度(本站工具設 100,000 字元上限就是為了這個)
JS 圈通常用 (3) + (4),從寫法上避免問題。
JavaScript vs Python:常被踩的差異
- 行首行尾:JS 沒有 \A \Z,要用 ^ $ + m flag
- Unicode:JS 要加 u flag 才支援 \p{Letter},Python 預設就支援
- Lookbehind:舊版 Safari < 16.4 完全不支援 lookbehind,你寫了線上就掛。一定要在 production 加 try / catch fallback
- re vs regex 模組(Python):標準 re 不支援 lookbehind 變長,要裝 regex 模組
- Sticky flag y:JS 有、Python 沒有 — 從固定位置開始 match,寫 lexer 用得到
QA 真實場景
我自己常用 regex 的時候:
- nginx access log 解析:抓 IP、status、response time → 算 p95(可以接到 [百分位計算工具](/zh-TW/tools/percentile))
- API response body 驗證:Robot Framework 的 Should Match Regexp 比 Should Contain 強太多
- 產測試資料:(\d{4}) (\d{4}) (\d{4}) (\d{4}) 這種格式驗證,要先確認自己產的 [信用卡測試資料](/zh-TW/tools/tw-test-data) 符合
- Selenium 動態元素 ID:抓 userdata-([a-f0-9]{8}) 這種隨機後綴
- 錯誤訊息分類:把 stack trace 用 regex 抓出 file path + line number 統計最常出問題的模組
想試?把這篇用到的所有 pattern 貼到 [Regex 工具](/zh-TW/tools/regex) 跑,即時看命中。最常出意外的就是 lookbehind 在 Safari 上掛掉那個。