讀古今文學網 > 精通正則表達式(第3版) > 「缺失」的preg函數 >

「缺失」的preg函數

\"Missing\"Preg Functions

PHP內建的preg函數已經提供了繁多的功能,但是有時候我仍然發現它們不夠用。一個例子是我自己開發的preg_match(☞454)。

我發現,另一類需要提供自己的支持函數的情形是,正則表達式不是在程序內部通過pattern參數字符串提供的,而是來自程序外部(例如,從文件讀入,或者是在Web表單中提交)。下一節我們將會看到,把純粹的正則表達式字符串轉換為適合 patern 參數使用的形式也很複雜。

同樣,在使用這些正則表達式之前,通常都必須驗證他們的語法正確性。我同樣會講解這些問題。

與本書中的所有程序代碼一樣,下一頁的函數也可以從我的網站下載:http://regex.info/。

preg_regex_to_pattern

如果正則表達式包含在字符串中(可能是從配置文件讀入,或者通過 Web 表單提交),在preg函數中使用時,首先必須在兩端加上分隔符,才能生成一個preg函數能用的pattern參數。

問題所在

許多時候,把正則表達式轉換為pattern參數只不過是在兩端加上斜線而已。這樣,正則表達式字符串『[a-z]+』 就成了『/[a-z]+/』,可以用作preg函數的pattern參數的字符串。

如果正則表達式中包含用作分隔符的字符,情況就很複雜。例如,正則表達式是『^http://([^/:]+)』,僅僅在兩端添加反斜線得到『/^http://([^/:]+)/』,用作pattern時,結果會是「Unknown modifier/」。

第 448 頁已經介紹過,這個錯誤信息是因為字符串中的前兩個斜線字符被當作分隔符,之後的部分(在這裡就是第3個斜線之後的部分)被當作修飾符序列了。

解決之道

有兩種辦法能解決內嵌分隔符的問題。之一是選擇正則表達式中沒有出現的分隔符,如果需要手工構造pattern-modifier字符串,那麼這當然是推薦的辦法了。所以我在第444、449和450頁(還有許多)的例子中使用{…}作為分隔符。

要選出正則表達式中沒有出現的分隔符可能並不容易(甚至不可能),因為字符串中可能包含所有分隔符,或者你不能預先知道需要處理的文本。在實際應用正則表達式時這需要特別關注,所以最簡單的辦法是使用第二種:選擇一個分隔符,然後對正則表達式字符串中出現的此分隔符進行轉義。

問題可能比初看起來要困難許多,因為你必須關注某些重要的細節。例如,在目標字符串末尾的轉義必須進行特殊處理,保證它不會轉義緊跟在後面的分隔符。

下面的函數接收正則表達式字符串,以及可能出現的 pattern-modifier 字符串,返回一個可以用於preg函數的pattern字符串。代碼中難看的反斜線(正則表達式和PHP子串轉義)或許是你見過的最複雜的表示;這段代碼並不容易讀懂(如果你希望補習PHP單引號字符串的語意,請參考第444頁)。

每次需要的時候重新寫這個函數太麻煩,於是我將它封裝到一個函數中(我希望它會成為preg套件中的內建函數)。

有興趣的讀者不妨想想函數尾部preg_replace_callback使用的正則表達式:它如何工作,以及回調函數如何遍歷整個pattern字符串,轉義每個未轉義的斜線,而不修改已轉義的斜線。

對未知的Pattern參數進行語法檢查

Syntax-Checking an Unknown Pattern Argument

在正則表達式兩端添加分隔符之後,我們確信它適於用作preg函數的pattern參數了,但是原來的正則表達式還沒有經過語法正確性檢驗。

舉例來說,如果原始的正則表達式是『*.txt』,因為某些人希望使用文件群組功能(☞4)而不是正則表達式,那麼preg_regex_to_pattern返回的就是/*.txt/。這個正則表達式當然不合法,所以程序會發出警報(如果啟用了警報功能):

Compilation failed:nothing to repeat at offset 0

PHP沒有內建檢測pattern參數及其正則表達式的語意是否合法的函數,不過我為讀者提供了一個。

preg_pattern_error會對pattern 參數進行簡單測試,試圖使用此正則表達式——在函數當中有一行調用preg_match。函數的其他部分使用關注PHP的管理功能處理preg_match可能顯示的錯誤信息。

對未知正則表達式進行語法檢查

Syntax-Checking an Unknown Regex

這個函數使用剛剛開發的功能來檢查一個純正則表達式(沒有分隔符也沒有模式修飾符)的語法。如果語法不正確,會返回對應的錯誤信息,如果語法正確,返回false。