正則表達式優化
——《精通正則表達式》閱讀筆記
[TOC]
第4章:表達式的匹配原理
引擎
DFA (Deterministic Finite Automaton 確定有窮自動機):
常見的只有MySQL,文本主導,不支持反向引用和捕獲括號,但快
傳統型 NFA(Non-非):
大多數語言,表達式主導,編譯快,內存少,寫法不同有性能差異
標準 POSIX NFA:
leftmost-longest,嘗試所有確保最長
混合 Tcl 等:
Perl、Python、Go(leftmost-first)
規則
最左優先,儘可能多(匹配優先)
回溯
NFA 有兩個可能時會根據 匹配優先*
還是 忽略優先*?
走其中一個分支,並保存備用狀態
如果不成功再回溯嘗試另一個分支
第5章:正則表達式實用技巧
(多選|分支)排序可能影響匹配結果
第6章:打造高效正則表達式
減少測試和回溯
- 如果順序不影響結果時更多匹配的放前面
- 編譯
- 傳動(從第1個字符開始,從第2個字符開始...)
- 檢測(相連 量詞{m,n}+* (捕獲))
- 成功/->2.傳動
- 失敗
常見措施
編譯優化
- 緩存
傳動優化
- 錨點(行始
^ \A
起始\G
行末$ \Z \z
) - 隱式錨點(
.* .+
開始) - 開始字符
====
比={4}
快100倍 - 內嵌字符(Boyer-Moore字符串檢索算法後前移, 需要前面固定個數)
- 長度小於時不運行
正則優化
- 連接當做整體
-
.*
特殊優化比(?:.)*
快(Java 10% Python 50倍) - 消除沒必要的括號
- 消除沒必要的[字符組]
- 忽略優先量詞
*?
(儘可能少)通常比匹配優先量詞慢 - 限制回溯,避免括號內外都是量詞
- 避免指數級(超線性)匹配
- 使用佔有優先量詞(+不會回溯)減少狀態
-
\d{4}
量詞優化比\d\d\d\d
快(Java 幾倍 Python 20%) - 引擎識別捕獲括號是否需要
訣竅
-
xx*
比x+
能適應的優化更多 - 手工模擬優化
-
(000|999)$
比關閉結束錨點優化的(?:000|999)$
快(Perl 幾千倍) - 避免重新編譯,Perl避免用變量插值
- 使用(?:非捕獲型括號)
- 不要濫用括號,如上面的
.*
比(?:.)*
快 - 不要濫用字符組,
[.]
應該用\.
- 不區分大小寫效率低已經修正
- 使用起始錨點
.*
開頭的前面加^
或\A
- 從量詞中提取:
xx*
替代x*
,-----{0,2}
替代-{5,7}
- 提取開頭:
th(is|at)
替代(this|that)
- 將錨點獨立出來:
^(?:abc|123)
替代^abc|^123
,^(abc)
替代(^abc)
- 末尾獨立出
$
- 接近開頭忽略優先
*?
,接近結尾匹配優先 - 拆分成多個正則
- 使用(?>固化分組)和佔有優先量詞
*+
- 最可能匹配的分支放前面(POSIX 會全部嘗試取最長就不需要)
- 結尾部分分散到各個部分(有些系統不需要如Perl的
$
)
消除循環
"(\\.|[^\\"]+)*"
優化爲:
"[^\\"]*(\\.[^\\"]*)*"
公式:
opening normal* (special normal*) closing
左 常規*(特殊 常規*)* 右
- 常規和特殊的開頭不能重合
- 特殊部分必須匹配至少一個字符
- 特殊部分必須是固化的
方法2:[^\\"]
匹配更多,如果是轉義,後面繼續,結果一樣
方法3:匹配主機名
[a-z]+(\.[a-z]+)*
使用佔有優先量詞
"([^\\"]++|\\.)*+"
使用固化分組
"(?>(?>[^\\"]+|\\.)*)"
\G(?:^|,)(?:((?>[^"]*)(?>""[^"]*)*)|([^",]*))
消除註釋
/\*.*?\*/
/\*([^*]|\*+[^/*])*\*+/
消除循環
/\*[^*]*\*+(?:[^/*][^*]*\*+)*/
流暢運轉
塊註釋=/\*[^*]*\*+(?:[^/*][^*]*\*+)*/
行註釋=//[^\n]*
雙引號="[^\\"]*(\\.[^\\"]*)*"
單引號='[^\\']*(\\.[^\\']*)*'
(雙引號|單引號)|塊註釋|行註釋
替換爲
$1
優化爲:
開頭集=[^"'/]
(雙引號|單引號|開頭集+)|塊註釋|行註釋
優化爲:
(開頭集+|雙引號|單引號)|塊註釋|行註釋
優化爲:
(開頭集+|雙引號 開頭集*|單引號 開頭集*)|塊註釋|行註釋