正規表示式測試

用自己的文本測試 JavaScript 正規表示式。可切換 g、i、m、s、u 標誌,檢視每個匹配的位置與捕獲組,並即時預覽替換結果。

邊輸入邊看到每一個匹配、每一個分組和每一個位置。 輸入模式和待匹配的文本。每條結果都會列出位置和捕獲組;模式非法時會顯示引擎自身的報錯資訊;替換框則精確展示一次 replace 呼叫會產生什麼。

/ / g
匹配結果

像引擎那樣讀一條正規表示式

標誌改變的是含義,而不只是搜尋方式

這五個標誌不是偏好設定,它們會重新定義整個模式。沒有 g 時,搜尋找到第一個匹配就停止;加上它,引擎會維護一個 lastIndex 游標遍歷整個字串——這也是同一個全域性正則在多次呼叫間複用時,看起來會「跳過」匹配的原因。i 標誌按 Unicode 大小寫對映摺疊大小寫,而不是簡單的 ASCII 位移。

最容易被誤解的是 msm 只改變 ^$ 的含義,把它們錨定到行邊界而非整個字串,對 . 毫無影響;反過來 s 只改變 .,讓它能匹配換行符。記住 "dotall" 這個名字最有用。u 標誌讓模式按碼點而非 UTF-16 碼元工作,於是 . 能把一個 emoji 當作一個整體匹配,\u{1F600} 這樣的轉義也變得合法。它同時會把過去被容忍的轉義變成錯誤——這就是給已有模式加上它會突然報錯的原因。

分組、位置與替換

每一對圓括號預設都會捕獲,按左括號出現的順序編號,替換時的 $1$2 引用的正是這些編號。巢狀遵循同樣的規則,所以在 ((a)b) 中第一組是 ab,第二組是 a。當你只是為了分支或重複而需要分組時,用 (?:...) 可以不分配捕獲,也讓編號更易讀。

每個匹配所報告的位置,是首個被匹配字元的偏移量,其意義比聽上去更大。它讓你能區分位於不同位置的兩個相同匹配,確認錨點是否如預期生效,並診斷經典的零長度匹配。像 \d* 這樣的模式在任何地方都能匹配空串,於是全域性掃描會在每個位置都找到一個匹配;引擎之所以還能推進,是因為實現會在匹配為空時強制把游標前移。

回溯與匹配上限

包括 JavaScript 在內的多數引擎使用回溯。當模式的某一部分失敗時,引擎會退回上一個決策點嘗試另一條路徑。通常這不可見。一旦巢狀量詞讓路徑數量指數級增長,它就會變成災難,經典形態是用 (a+)+b 去匹配一長串沒有 ba。這種輸入能讓瀏覽器標籤頁卡死;如果模式來自使用者輸入,它就成了拒絕服務的攻擊面。

兩個習慣幾乎能規避全部問題:優先使用具體的字元類而非 .*,以免多個分支互相重疊;能加錨點就加錨點,因為帶錨點的失敗會被立即否定,而不是在每個偏移位置重試一遍。本工具還把結果上限設為 500 條並在截斷時給出提示,這樣失控的模式只會退化為較慢的結果,而不是凍結頁面。

開源說明:使用原生 JavaScript 實現,不依賴第三方庫。

常見問題

這裡用的是哪種正則方言?
JavaScript,也就是瀏覽器內建的 ECMAScript 引擎。Perl、PCRE 和 Python 的寫法接近,但在後行斷言、命名分組和部分轉義上有差異。
g 標誌到底改變了什麼?
不加時只返回第一個匹配;加上後引擎會掃描整個字串並返回全部匹配,這裡的結果列表正是這樣填充的。
為什麼會出現空匹配?
因為你的模式可以匹配零個字元,例如 `\d*`。全域性掃描於是在每個位置都報告一個匹配,引擎通過手動前移游標來避免死迴圈。
替換時如何引用捕獲組?
用 $1 引用第一組、$2 引用第二組,$& 表示整個匹配。若需要字面的美元符號,請寫 $$。
「僅顯示前 500 個」是什麼意思?
結果列表上限為 500 條,避免過於寬泛的模式凍結頁面。替換預覽仍然作用於全文。
為什麼我的模式讓頁面變卡?
像 (a+)+ 這樣的巢狀量詞會導致指數級回溯。請用具體字元類替代 .*,並給模式加上錨點,讓匹配保持線性。