常見問題
正則表達式是一個很是強大的工具,但在有些時候它並不能直觀地按照你的意願來運行。本篇咱們將指出一些最多見的錯誤。
使用字符串方法
有時使用 re 模塊是個錯誤!若是你匹配一個固定的字符串或者單個字符類,而且你沒有使用 re 的任何標誌(像 IGNORECASE 標誌),那麼就沒有必要使用正則表達式了。字符串有一些方法是對固定字符串進行操做的,而且它們一般比較快。由於它們都是獨立優化的 C 語言小循環,目的是在簡單的狀況下代替功能更增強大、更具通用性的正則表達式引擎。
舉個例子,例如你想把字符串中全部的 dead 替換成 word,你會想到使用正則表達式的 re.sub() 方法來實現,但這麼簡單的替換,仍是考慮直接使用字符串的 replace() 方法吧。但有一點你須要注意,就是 replace() 會在單詞裏邊進行替換,像swordfish 會變成 sdeedfish,這顯然不是你想要的!replace() 沒辦法識別單詞的邊界,所以你纔來考慮使用正則表達式。只須要將 RE 的模式寫成 \bword\b 便可勝任此任務。
另外一個常見的狀況是從一個字符串中刪除單個字符或者用另外一個字符替代它。你也許會想到用 re.sub('\n', ' ', S) 這樣的正則表達式來實現,但其實字符的 translate() 方法徹底可以勝任這個任務,而且比任何正則表達式操做起來更快些。
簡而言之,在使用 re 模塊以前,先考慮一下你的問題是否能夠用更快速、簡單的字符串自帶方法來解決。
match() VS search()
match() 函數只會檢查 RE 是否在字符串的開始處匹配,而 search() 會遍歷整個字符串搜索匹配的內容。記住這一區別很重要。再次強調一下,match() 只會報告一次成功的匹配,而且匹配的位置必須是從字符串的第一個字符開始:
html
另外一方面,search() 函數將遍歷整個字符串,並報告它找到的第一個匹配:
正則表達式
有時候你可能會耍點小聰明,使用 re.match() 而後在 RE 的前邊加上 .*。但儘可能不要這麼作,最好採用 re.search() 代替。正則表達式編譯器會對 REs 作一些分析,以即可以在搜索匹配時提升速度。通常分析會先找到匹配的第一個字符是什麼。舉個例子,模式 Crow 必須從字符 'C' 開始匹配,那麼匹配引擎分析後會快速遍歷字符串,而後在 'C' 被找到以後纔開始所有匹配。
按照上面的分析,你添加一個 .* 會致使這個優化失敗,這就須要從頭至尾掃描一遍,而後再回溯匹配 RE 剩餘的部分。因此,請使用 re.search() 代替。
貪婪 VS 非貪婪
當重複一個正則表達式時,若是使用 a*,那麼結果是儘量多地去匹配。當你嘗試匹配一對對稱的定界符,例如 HTML 標誌中的尖括號,默認的貪婪模式會使得你很困擾。
咱們來看下例子:
函數
RE 匹配在 <html> 的 < 後,.* 消耗掉字符串的剩餘部分。因爲正則表達式默認是貪婪的緣由,RE 必須從字符串的尾部一個字符一個字符地回溯,直到找到匹配的 >。你們看到,按照這種方法,最後找到匹配內容竟是 <html> 的 < 開始,到</title> 的 > 結束。顯然這不是你想要的結果。
在這種狀況下,解決方案是使用非貪婪的限定符 *?、+?、?? 或 {m, n}?,儘量地匹配小的文本。
工具
在上邊的例子中,> 在第一個 < 被匹配後馬上嘗試匹配,若是失敗,匹配引擎前進一步,嘗試下一個字符,直到第一次匹配 >,這樣就獲得了咱們想要的結果。
注意,使用正則表達式分析 HTML 和 XML 是很痛苦的。當你編寫一個正則表達式去處理全部可能的狀況時,你會發現 HTML 和 XML 總會打破你的「規則」,這讓你很頭疼......像這樣的話,建議使用 HTML 和 XML 解析器來處理更合適。
使用 re.VERBOSE
如今你應該意識到了,正則表達式的表示很是緊湊。這也帶來了一個問題,就是很差閱讀。中等複雜的正則表達式可能包含許多反斜槓、圓括號和元字符,以致於難以讀懂。
在這些 REs 中,當編譯正則表達式時指定 re.VERBOSE 標誌是很是有幫助的。由於它容許你能夠編輯正則表達式的格式,使之更清楚。
re.VERBOSE 標誌有幾個做用。在正則表達式中不在字符類中的空白字符將被忽略。這就意味着像 I love FishC 這樣的表達式和可讀性較差的 IloveFishC 相同。但 [a b] 將匹配字符 'a'、'b' 或 ' ';另外,你也能夠把註釋放到 RE 中,註釋是從 # 開始到下一行。當使用三引號字符串時,會使得 REs 的格式更整潔:
pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)
一樣的內容,下邊這個要難讀得多:
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")
<完>優化