這個測試器直接用瀏覽器自己的 JavaScript RegExp 引擎跑你的 pattern,邊打字邊在測試文字上高亮所有匹配,並逐筆列出匹配內容、在字串中的起始位置,以及 named group 抓到的值。因為引擎就是瀏覽器本身,你在這裡驗證過的結果和你寫進 .js / .ts 的結果一致——但同樣的道理,它不是 PCRE、不是 Python 的 re,更不是 Go 的 RE2。pattern 與測試文字都留在這個分頁裡,不會被送出去。
使用步驟
- 在上方「正規表達式」輸入框裡打 pattern 本體。左右兩側的
/只是裝飾,不要把/和結尾的 flag 一起貼進來,否則斜線會被當成你要匹配的字元。也不需要像寫new RegExp('\\d')那樣把反斜線加倍,直接打\d就好。 - 勾選 flag:
g全局(預設開啟)、i不分大小寫、m多行(讓^與$對每一行生效)、s點匹配換行(讓.也吃\n)。輸入框右側的灰字會即時顯示目前生效的 flag 組合。 - 把樣本貼進「測試文字」區塊。可以是多行的 log、CSV 片段或一整段 HTML,高度可以拖曳調整。
- pattern 語法有錯時,輸入框下方會直接顯示引擎回報的原始訊息,例如
Unterminated group或numbers out of order in {} quantifier。這段文字來自瀏覽器,不是本工具改寫過的。 - 語法通過後,「匹配結果高亮」會把測試文字整份重印一次並把命中的片段標色,標題括號裡是匹配總數。
- 下方「匹配列表」逐筆列出:編號、以 JSON 字串形式呈現的匹配內容(所以換行、空白會顯示成看得見的
\n)、index起始位置,以及當你使用(?<name>...)時的groups。列表超過高度會變成可捲動。
什麼時候用得上
在寫進程式碼前先確認 pattern
要抓 Nginx access log 的 IP、驗證表單裡的手機號碼、從 commit message 撈出 issue 編號——這些 pattern 在這裡跑一次,行為會和之後放進前端程式或 Node 腳本裡完全相同,不必為了改一個字元重新跑一輪 build。
用 named group 拆解結構化字串
把 (?<date>\d{4}-\d{2}-\d{2}) (?<level>\w+) (?<msg>.*) 貼上,匹配列表的 groups 欄位會直接顯示每一筆抓到的欄位名稱與值,比在 console 裡一個個印 match[1]、match[2] 快,也不會因為之後在中間插一個括號就整串錯位。
找出「為什麼多抓了 / 少抓了」
貪婪的 .* 一路吃到行尾、| 的優先順序不如預期、字元集裡少了一個減號——這類問題單看 pattern 看不出來。高亮會直接顯示邊界落在哪,index 則告訴你兩個相鄰匹配之間是不是漏掉了一段。
檢查外面抄來的 pattern 能不能用在 JS
Stack Overflow 和部落格上的 regex 常常是 PCRE 或 Python 語法。貼進來就知道會不會被引擎拒絕:(?P<name>...)、(?>...)、a++、(?i) 在 JavaScript 一律報 Invalid group 或 Nothing to repeat。
限制與注意事項
- Catastrophic backtracking 真的會凍住分頁。JavaScript 用的是回溯式引擎,不像 Go 的 RE2 保證線性時間:
(a+)+b對 28 個a在 V8 上要跑約 18 秒,32 個a要約 38 秒,每多一個字元時間就翻倍。這個工具在每次按鍵時同步重跑一次比對,沒有 timeout、也沒有中斷按鈕,一旦踩到只能關掉分頁。巢狀量詞((x+)*、(a|a)*、(\d+)+$)請先在很短的字串上試。 - 只支援
g、i、m、s四個 flag,沒有u/v(Unicode 模式)、y(sticky)與d(hasIndices)。少了u不只是少一個選項:\p{L}、\p{Script=Han}這類 Unicode property escape 不會報錯,而是被當成字面字元p{L}去匹配,錯得毫無聲響。 - 匹配列表只顯示 named group。編號 group(
$1、$2)不會出現,想看某個括號抓到什麼,請把它改寫成(?<name>...)。另外groups是用JSON.stringify印出來的,沒有參與匹配的 named group 值是undefined、會被直接省略,所以/(?<a>x)|(?<b>y)/對y只會顯示{"b":"y"},看不到a。 index是 UTF-16 code unit 的位置,不是「第幾個字」。emoji 與其他 BMP 之外的字元各佔 2 個單位,所以在👍b裡b的 index 是 2 而不是 1;同理,沒有uflag 時.一次只吃半個 emoji。\d、\w、\b全部只認 ASCII。全形數字123不會被\d+匹配,\w+抓不到abc,\b\w+\b對中日韓文字完全無效。要處理中文得自己寫範圍,例如[\u4e00-\u9fff]+。- 這是純測試工具:沒有取代(replace)預覽、沒有 pattern 結構圖、沒有常用 pattern 範例庫,也沒有匯出或複製按鈕,pattern 不會被記住,重新整理就空了。所有運算都在瀏覽器裡完成,pattern 與測試文字不會送到任何伺服器。
常見問題
從 Python 或 PHP 抄來的 pattern 為什麼在這裡報錯?
JavaScript 的 RegExp 語法比 PCRE 小一圈。會直接報錯的有:named group 寫成 Python 的 (?P<name>...)(JS 是 (?<name>...))、atomic group (?>...)、possessive 量詞 a++、inline flag (?i)、註解群組 (?#...)、遞迴 (?R)。更麻煩的是那些「不報錯但行為不同」的:\A、\Z 在 JS 裡不是錨點而是字面的 A、Z,POSIX 字元類 [[:alpha:]] 會被解讀成「:、a、l、p、h 這幾個字元的集合再接一個 ]」。兩者都不會跳錯誤訊息,只會靜靜地匹配不到你要的東西——這種情況要靠高亮看出來。
lookbehind 可以用嗎?
可以。(?<=...) 與 (?<!...) 在 V8(Chrome、Edge)自 2018 年、Firefox 78、Safari 16.4 起支援。而且 JavaScript 允許變長 lookbehind——(?<=ab+)c 是合法的,這點反而比 Python 的 re 寬鬆(re 要求固定長度)。代價是這種 pattern 在很舊的 Safari 上會直接變成語法錯誤,所以要上線的話記得確認你的瀏覽器支援範圍。
取消 `g` 之後為什麼只剩一個匹配?
這是刻意的。g 關掉時只回報第一個匹配,等同你在程式裡對 re.exec(text) 呼叫一次的結果。順帶提一個相關的坑:如果你在自己的程式碼裡重複使用同一個帶 g 的 RegExp 物件,lastIndex 會在呼叫之間累積,第二次 test() 同樣的字串可能突然回傳 false。這個工具每次都重新建立 RegExp,所以不會遇到,但你的程式會。
匹配數說有 5 個,可是高亮區完全沒有變色?
你的 pattern 匹配到了長度為 0 的字串,常見的有配上 m 的 ^、\b、或是 a* 這種可以匹配空字串的量詞。零長度匹配在「匹配列表」裡會顯示成 "" 並帶著 index,但高亮沒有任何字元可以標色。這也是為什麼工具遇到零長度匹配時會手動把搜尋位置往前推一格——不推的話 exec 會在同一個位置無限迴圈。
我貼的測試文字會被上傳嗎?
不會。整個工具就是在瀏覽器裡建立一個 new RegExp(pattern, flags) 再跑 exec() 迴圈,程式碼裡沒有任何網路請求。你可以把網路斷開再操作一次驗證——功能完全正常。不過還是提醒一句:真的含有客戶資料的 log,測試時建議先把敏感欄位換成假值,因為你的 pattern 本身可能會被你複製到別的地方去。
`$` 為什麼配不到最後一行?`.` 為什麼跨不過換行?
兩個各自獨立的規則。沒有 m 時,JS 的 $ 只匹配整份字串的最末端,而且不像 Python 的 $ 會容許一個結尾換行:/abc$/ 對 "abc\n" 回傳 false,勾上 m 才會是 true。至於 .,它預設就不匹配 \n(也不匹配 \r 與 Unicode 行分隔符),要它吃換行請勾 s。