Python 正則表達式之三:貪婪

dgvk5778 7年前發布 | 14K 次閱讀 正則表達式 Python Python開發

默認情況下,正則表達式將進行貪婪匹配。所謂“ 貪婪 ”,其實就是在多種長度的匹配字符串中,選擇較長的那一個。例如,如下正則表達式本意是選出人物所說的話,但是卻由于“ 貪婪 ”特性,出現了匹配不當:

>>> sentence = """You said "why?" and I say "I don't know"."""
>>> re.findall(r'"(.*)"', sentence)
['why?" and I say "I don\'t know']

再比如,如下的幾個例子都說明了正則表達式“ 貪婪 ”的特性:

>>> re.findall('hi*', 'hiiiii')
['hiiiii']
>>> re.findall('hi{2,}', 'hiiiii')
['hiiiii']
>>> re.findall('hi{1,3}', 'hiiiii')
['hiii']

非貪婪

當我們期望正則表達式“ 非貪婪 ”地進行匹配時,需要通過語法明確說明:

{2,5}? 捕獲2-5次,但是優先次數少的匹配

在這里,問號 ? 可能會有些讓人犯暈,因為之前他已經有了自己的含義:前面的匹配出現0次或1次。其實,只要記住,當問號出現在表現 不定次數 的正則表達式部分之后時,就表示非貪婪匹配。

還是上面的那幾個例子,用非貪婪匹配,則結果如下:

>>> re.findall('hi*?', 'hiiiii')
['h']
>>> re.findall('hi{2,}?', 'hiiiii')
['hii']
>>> re.findall('hi{1,3}?', 'hiiiii')
['hi']

另外一個例子中,使用非貪婪匹配,結果如下:

>>> sentence = """You said "why?" and I say "I don't know"."""
>>> re.findall(r'"(.*?)"', sentence)
['why?', "I don't know"]

捕獲與非貪婪

嚴格來說,這一部分并不是非貪婪特性。但是由于其行為與非貪婪類似,所以為了方便記憶,就將其放在一起了。

(?=abc) 捕獲,但不消耗字符,且匹配abc

(?!abc) 捕獲,不消耗,且不匹配abc

在正則表達式匹配的過程中,其實存在“消耗字符”的過程,也就是說,一旦一個字符在匹配過程中被檢索(消耗)過,后面的匹配就不會再檢索這一字符了。

知道這個特性有什么用呢?還是用例子說明。比如,我們想找出字符串中出現過1次以上的單詞:

>>> sentence = "Oh what a day, what a lovely day!"
>>> re.findall(r'\b(\w+)\b.*\b\1\b', sentence)
['what']

這樣的正則表達式顯然無法完成任務。為什么呢?原因就是,在第一個(\w+)匹配到what,并且其后的\1也匹配到第二個what的時候,“Oh what a day, what”這一段子串都已經被正則表達式消耗了,所以之后的匹配,將直接從第二個what之后開始。自然地,這里只能找出一個出現了兩次的單詞。

那么解決方案,就和上面提到的(?=abc)語法相關了。這樣的語法可以在分組匹配的同時,不消耗字符串!所以,正確的書寫方式應該是:

>>> re.findall(r'\b(\w+)\b(?=.*\b\1\b)', sentence)
['what', 'a', 'day']

如果我們需要匹配一個至少包含兩個不同字母的單詞,則可以使用(?!abc)的語法:

>>> re.search(r'([a-z]).*(?!\1)[a-z]', 'aa', re.IGNORECASE)
>>> re.search(r'([a-z]).*(?!\1)[a-z]', 'ab', re.IGNORECASE)
<_sre.SRE_Match object; span=(0, 2), match='ab'>

Python正則表達式中關于貪婪的內容至此就總結完畢了。下一篇文章,我會繼續總結一下Python正則表達式re模塊的一些API的用法。

 

 

來自:http://blog.guoyb.com/2017/03/01/python-regex-3/

 

 本文由用戶 dgvk5778 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!