正則表達式——濫用點號的問題


因為點號能匹配幾乎所有的字符,所以實際應用中許多人圖省事,隨意使用.*.+,結果卻事與願違,下面以雙引號字符串為例來說明。

之前我們使用表達式"[^"]*"匹配雙引號字符串,而"圖省事"的做法是".*"。通常這么用是沒有問題的,但也可能有意外,例2-12就說明了一種如此。

例2-12  "圖省事"的意外結果

#字符串的值是"quoted string"  
print re.search(r"\".*\"", "\"quoted string\"").group(0)  
"quoted string"  
#字符串的值是"quoted string" and another"  
print re.search(r"\".*\"", "\"quoted string\" and another\"").group(0)  
"quoted string" and another"  `

用".*"匹配雙引號字符串,不但可以匹配正常的雙引號字符串"quoted string",還可以匹配格式錯誤的字符串"quoted string" and another"。這是為什么呢?

這個問題比較復雜,現在只簡要介紹,以說明圖省事導致錯誤的原因,更深入的原因涉及正則表達式的匹配原理,在第8章詳細介紹。

在正則表達式".*"中,點號.可以匹配任何字符,*表示可以匹配的字符串長度沒有限制,所以.*在匹配過程結束以前,每遇到一個字符(除去無法匹配的\n),.*都可以匹配,但是到底是匹配這個字符,還是忽略它,將其交給之后的"來匹配呢?

答案是,具體選擇取決於所使用的量詞。在正則表達式中的量詞分為幾類,之前介紹的量詞都可以歸到一類,叫做匹配優先量詞(greedy quantifier,也有人翻譯為貪婪量詞 )。匹配優先量詞,顧名思義,就是在拿不准是否要匹配的時候,優先嘗試匹配,並且記下這個狀態,以備將來"反悔"

來看表達式".*"對字符串"quoted string"的匹配過程。

一開始,"匹配",然后輪到字符q,.*可以匹配它,也可以不匹配,因為使用了匹配優先量詞,所以.*先匹配q,並且記錄下這個狀態【q也可能是.*不應該匹配的】;

接下來是字符u,.*可以匹配它,也可以不匹配,因為使用了匹配優先量詞,所以.*先匹配u,並且記錄下這個狀態【u也可能是.*不應該匹配的】;

……

現在輪到字符g,.*可以匹配它,也可以不匹配,因為使用了匹配優先量詞,所以.*先匹配g,並且記錄下這個狀態【g也可能是.*不應該匹配的】;

最后是末尾的",.*可以匹配它,也可以不匹配,因為使用了匹配優先量詞,所以.*先匹配",並且記錄下這個狀態【"也可能是.*不應該匹配的】。

這時候,字符串之后已經沒有字符了,但正則表達式中還有"沒有匹配,所以只能查詢之前保存備用的狀態,看看能不能退回幾步,照顧"的匹配。查詢到最近保存的狀態是:【"也可能是.*不應該匹配的】。於是讓.*"反悔"對"的匹配,把"交給",測試發現正好能匹配,所以整個匹配宣告成功。這個"反悔"的過程,專業術語叫做回溯(backtracking),具體的過程如圖2-1所示。

 

 
圖2-1  表達式".*"對字符串"quoted string"的匹配過程

 

如果把字符串換成"quoted string" and another",.*會首先匹配第一個雙引號之后的所有字符,再進行回溯,表達式中的"匹配了字符串結尾的字符",整個匹配宣告完成,過程如圖2-2所示。

 
圖2-2  表達式".*"的匹配過程


如果要准確匹配雙引號字符串,就不能圖省事使用".*",而要使用"[^"]*",過程如圖2-3所示。

 

 
圖2-3  表達式"[^"]*"的匹配過程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM