讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議44:正確使用原子組 >

建議44:正確使用原子組

正則表達式引擎支持一種稱做原子組的屬性。原子組寫作(?>…),也稱為「貪婪」子表達式,省略號表示任意正則表達式模板、非捕獲組和一個特殊的扭曲。存在於原子組中的正則表達式組中的任何回溯點都將被丟棄。這就為HTML正則表達式的回溯問題提供了一個更好的解決辦法:如果將[\s\S]*?序列和它後面的HTML標記一起放在一個原子組中,所需的HTML標籤被發現一次,這次匹配基本上就被鎖定了。如果正則表達式的後續部分匹配失敗,原子組中的量詞沒有記錄回溯點,那麼[\s\S]*?序列就不能擴展到已匹配的範圍之外。

但是,JavaScript不支持原子組,也不提供其他方法消除不必要的回溯。不過,可以利用前瞻過程中一項鮮為人知的行為來模擬原子組:前瞻也是原子組。不同的是,前瞻在整個匹配過程中不消耗字符,前瞻只是檢查自己包含的模板是否能在當前位置匹配。然而,可以避開這點,在捕獲組中包裝一個前瞻模板,在前瞻之外向它添加一個後向引用。


(?=(pattern to make atomic))\1


在任何使用原子組的模式中這個結構都是可重用的。只要記住,需要使用適當的後向引用次數,如果正則表達式包含多個捕獲組。HTML正則表達式在使用此技術後的修改如下:


/<html>(?=([\s\S]*?<head>))\1(?=([\s\S]*?<title>))\2(?=([\s\S]*?

<\/title>))\3(?=([\s\S]*?<\/head>))\4(?=([\s\S]*?<body>))\5

(?=([\s\S]*?<\/body>))\6[\s\S]*?<\/html>/


如果沒有尾隨的</html>,那麼最後一個[\s\S]*?將擴展至字符串結束,正則表達式將立刻失敗,因為沒有回溯點可以返回。正則表達式每次找到一個中間標籤就退出一個前瞻,它在前瞻過程中丟棄所有回溯位置。下一個後向引用簡單地重新匹配前瞻過程中發現的字符,將它們作為實際匹配的一部分。