讀古今文學網 > 精通正則表達式(第3版) > 效率 >

效率

PHP Efficiency Issues

PHP的preg程序使用的PCRE是經過優化的NFA正則引擎,所以第4到6章介紹的許多技巧都可以直接應用。其中包括對關鍵部分進行性能測試,根據實際數據而不是從理論分析來比較程序的快慢。第6章給出了PHP的性能測試的例子(☞234)。

如果程序對時間要求很嚴格,請記住兩點,回調函數通常要比模式修飾符e更快(☞465),在太長的目標字符串中使用命名捕獲必須進行更多的數據拷貝。

程序運行中,遇到正則表達式會編譯,但是PHP 有一個容量高達4 096 個正則表達式的大型緩存(☞242),所以實際上,特殊的pattern字符串只需要在第一次遇到的時候編譯。

模式修飾符S值得單獨介紹:它會「研究(study)」一個正則表達式,試圖進行更快的匹配(它與Perl的study函數不相關,Perl的study函數研究的是目標字符串,而不是正則表達式☞359)。

模式修飾符S:「研究」

The S Pattern Modifier:"Study"

使用模式修飾符S告訴正則引擎,在應用這個正則表達式之前,花一點時間(注5)來研究,希望這些多花的時間是值得的。但是,也可能這樣做之後也不會提升速度,但是某些情況下,速度的提升是與數據規模相關的。

現在有良好的標準來判斷哪種情況下此功能具有價值:它是第 6 章所說的開頭字符組識別優化(☞247)的增強。

首先要告訴你的是,除非你希望對大規模的文本應用某個正則表達式,否則不太可能節省多少時間。只有把同一個表達式應用到大規模的文本,或者大量小規模文本時,才需要考慮模式修飾符S。

不使用模式修飾符S,標準優化

來看個簡單的例子「<(\w+)」。從這個表達式中我們可以看出,每次匹配必須以字符『<』開頭。正則引擎能夠(preg套件必然會這樣做)利用這一點,預先在目標字符串中搜索『<』,只在這些位置應用完整的正則表達式(因為匹配必須以「<」開頭,在其他位置進行匹配嘗試是徒勞的)。

使用簡單預搜索可以比按部就班應用整個正則表達式快得多,原因就在於這種優化。尤其是,需要搜索的字符在目標字符串中出現的次數越少,優化越明顯。同樣,正則引擎判斷第一個字符匹配失敗的工作量越大,優化越明顯。這種優化對「<i>|</i>|<b>|</b>」比對「<(\w+)」更明顯,因為在進行下一輪嘗試之前,正則引擎會嘗試搜索4個不同的多選分支,這樣可以減少許多工作量。

使用模式修飾符S進一步優化

preg 引擎足夠聰明,能把這種優化應用到大多數正則表達式,它們的匹配必須以某個字符開頭,就像上面的例子一樣。不過,模式修飾符 S 告訴引擎,對於可能以多個字符開頭的表達式,必須首先分析正則表達式來啟用這種優化。

這裡有幾個正則表達式的例子,其中有一些在本章中已經出現過,使用模式修飾符 S 的結果如下:

模式修飾符S沒有用處的場合

想想在哪些情況下模式修飾符S沒有用會很有其法:

●開頭有錨點的表達式(例如「^」和「\b」),或者一個錨點緊跟全局性多選分支。這限於當前的實現,「\b」的限制,理論上在未來的某些版本中可以去掉。

●能夠匹配空字符的表達式,例如「\S*」。

●表達式可以從任何字符開始匹配(或者是絕大多數字符),例如「(?:[^()]++|\((?R)\))* 」,請參考第 475 頁的例子。這個表達式能夠從除『)』之外的任何字符開始匹配,所以預先檢查一遍幾乎不會去掉任何開始的位置。

●開頭字符只有一種可能的表達式,因為它們已經進行了優化。

使用建議

使用模式修飾符 S 之後,preg 引擎花在預分析上的時間並不會太長,所以如果你希望對大量的文本應用正則表達式,無妨使用它。如果你覺得有機會使用,潛在的可能就值得嘗試。