正則表達式是一種更爲強大的字符串匹配、字符串查找、字符串替換等操做工具。上篇講解了正則表達式的基本概念和語法以及re
模塊的基本使用方式,這節來詳細說說 re
模塊做爲 Python 正則表達式引擎提供了哪些便利性操做。javascript
>>> import re複製代碼
正則表達式的全部操做都是圍繞着匹配對象(Match)進行的,只有表達式與字符串匹配纔有可能進行後續操做。判斷匹配與否有兩個方法,分別是 re.match() 和 re.search(),二者有什麼區別呢?html
match 方法從字符串的起始位置開始檢查,若是恰好有一個子字符串與正則表達式相匹配,則返回一個Match對象,只要起始位置不匹配則退出,再也不日後檢查了,返回 Nonejava
>>> re.match(r"b.r", "foobar") # 不匹配
>>> re.match(r"b.r", "barfoo") # 匹配
<_sre.SRE_Match object at 0x102f05b28>
>>>複製代碼
search 方法雖然也是從起始位置開始檢查,可是它在起始位置不匹配的時候會一直嘗試日後檢查,直到匹配爲止,若是到字符串的末尾尚未匹配,則返回 Nonepython
>>> re.search(r"b.r", "foobar") # 匹配
<_sre.SRE_Match object at 0x000000000254D578>
>>> re.match(r"b.r", "foobr") # 不匹配複製代碼
二者接收參數都是同樣的,第一個參數是正則表達式,第二個是預匹配的字符串。另外,不論是 search 仍是 match,一旦找到了匹配的子字符串,就馬上中止日後找,哪怕字符串中有多個可匹配的子字符串,例如正則表達式
>>> re.search(r"f.o", "foobarfeobar").group()
'foo'複製代碼
二者的差別使得他們在應用場景上也不同,若是是檢查文本是否匹配某種模式,好比,檢查字符串是否是有效的郵箱地址,則可使用 match 來判斷: 函數
>>> rex = r"[\w]+@[\w]+\.[\w]+$"
>>> re.match(rex, "123@qq.com") # 匹配
<_sre.SRE_Match object at 0x102f05bf8>
>>> re.match(rex, "the email is 123@qq.com") # 不匹配
>>>複製代碼
儘管第二個字符串中包含有郵件地址,但字符串總體不能看成一個郵件地址來使用,在網頁上填郵件地址時,顯然第二種寫法是無效的。工具
一般,search 方法可用於判斷字符串中是否包含有與正則表達式相匹配的子字符串,還能夠從中提出匹配的子字符串,例如:this
>>> rex = r"[\w]+@[\w]+\.[\w]+"
>>> m = re.search(rex, "the email is 123@qq.com .")
>>> m is None
False
>>> m.group()
'123@qq.com'
>>>複製代碼
細心的你可能已經發現了,上面例子與前面例子的正則表達式寫法有細微區別,前者多一個元字符 $
,它的目的是用於徹底匹配字符串。由於不加 $,那麼下面這種狀況用match方法也匹配,顯示這在表單驗證時是沒法知足要求的。 spa
>>> rex = r"[\w]+@[\w]+\.[\w]+"
>>> re.match(rex, "123@qq.com is my email")
<_sre.SRE_Match object at 0x10cadebf8>
>>>複製代碼
那麼有沒有可能不加$,就能夠判斷是否徹底匹配字符串呢?在 Python3 中,re.fullmatch
就能夠知足這樣的需求。.net
>>> rex = r"[\w]+@[\w]+\.[\w]+"
>>> re.fullmatch(rex, "123@qq.com is my email") # 不匹配
>>> re.fullmatch(rex, "123@qq.com") # 匹配
<_sre.SRE_Match object; span=(0, 10), match='123@qq.com'>複製代碼
雖然兩者均可以經過 group() 提取出匹配的子字符串,可是,若是字符串中有多個匹配的子字符串時,兩個方法都不行,由於它們都是在一旦匹配了第一個子字符串,就再也不日後匹配了。
>>> m = re.search(rex, "email is 123@qq.com, anthor email is abc@gmail.com !")
>>> m.group()
'123@qq.com'複製代碼
那麼如何把文本中的全部匹配的郵件地址提取出來呢?re 模塊爲咱們準備了 re.findall() 和 re.finditer() 這兩個方法,它們會返回文本中全部與正則表達式相匹配的內容。前者返回的是一個列表(list)對象,後者返回的是一個迭代器(iterator)。
>>> emails = re.findall(rex, "email is 123@qq.com, anthor email is abc@gmail.com")
>>> emails
['123@qq.com', 'abc@gmail.com']複製代碼
findall 返回的對象是由匹配的子字符串組成的列表,它返回了全部匹配的郵件地址。
>>> emails = re.finditer(rex, "email is 123@qq.com, anthor email is abc@gmail.com")
>>> emails
<callable-iterator object at 0x0000000002592390>
>>> for e in emails:
... print(e.group())
...
123@qq.com
abc@gmail.com複製代碼
finditer 返回的對象是由 Match 對象組成的迭代器,由於裏面的元素是Match對象,因此要獲取裏面的郵件地址還須要調用group方法來提取。關於列表和迭代器的區別,此文不作介紹,能夠查看公衆號「Python之禪」的歷史文章。
咱們都知道字符串有一個split方法,可根據某個子串分隔字符串,如:
>>> "this is a string.".split(" ")
['this', 'is', 'a', 'string.']複製代碼
但該方法有一個缺陷,好比上面的字符串,根據空格分隔字符串時,字符串後面多一個點,若是用 re.split 就能夠避免這種狀況。
>>> words = re.split(r"\W+", "this is a string.")
>>> words
['this', 'is', 'a', 'string', '']
>>> list(filter(lambda x: x, words))
['this', 'is', 'a', 'string']
>>>複製代碼
re.split是一種更爲高級的字符串分隔操做的方法。在這裏,split根據非字母正則來分隔字符串,但凡是 string.split 無法處理的問題,能夠考慮使用re模塊下的split方法來處理。此外,正則表達式中若是有分組括號,那麼返回結果又不一致,這個能夠留給你們查閱文檔,某些場景用得着。
re.split是一種更爲高級的字符串分隔操做的方法。在這裏,split根據非字母正則來分隔字符串,但凡是 string.split 無法處理的問題,能夠考慮使用re模塊下的split方法來處理。此外,正則表達式中若是有分組括號,那麼返回結果又不一致,這個能夠留給你們查閱文檔,某些場景用得着。
把全部郵箱地址替換成 admin@qq.com
>>> rex = r"[\w]+@[\w]+\.[\w]+" # 郵件地址正則
>>> re.sub(rex, "admin@qq.com", "234@qq.com, 456@qq.com ")
'admin@qq.com, admin@qq.com '
>>>複製代碼
另一個例子,就是上次講過的將 img 標籤的 src 路徑替換成絕對完整的URL地址
html = """
...
<img src="/images/category.png">
this is anthor words
<img src="http://foofish.net/images/js_framework.png">
"""複製代碼
若是用字符串的replace方法是無法實現了,這時須要用到正則表達式的 re.sub,正則表達式應用了非貪婪模式,使用了一個分組,用於提取 src 的路徑。
rex = r'.*?<img src="(.*?)".*?>'複製代碼
這裏咱們要把替換目標 repl 做爲函數來處理。
def fun(m):
img_tag = m.group()
src = m.group(1)
if not src.startswith("http:"):
full_src = "http://foofish.net" + src
else:
full_src = src
new_img_tag = img_tag.replace(src, full_src)
return new_img_tag複製代碼
引擎會自動把全部匹配的結果應用到該函數中,函數的參數就是每個匹配的Match對象,經過 group(1) 提取分組後判斷是否爲一個完整的URL路徑,只有是不完整的咱們才替換,不然仍是按照原來的方式返回。
new_html = re.compile(rex).sub(fun, html)
print(new_html)
# 輸出
...
<img src="http://foofish.net/images/category.png">
this is anthor words
<img src="http://foofish.net/images/js_framework.png">複製代碼
若是還想知道替換次數是多少,那麼可使用 re.subn
方法,這個方法具體使用能夠參考文檔,留着讀者本身思考。
此外,以上方法都有一個默認的 flag 參數,該參數用於改變匹配的行爲,經常使用的可選值有:
>>> re.match(r"foo", "FoObar", re.I)
<_sre.SRE_Match object; span=(0, 3), match='FoO'>
>>>複製代碼
以上介紹的都是 re 模塊下面的方法,其實,這些只不過是一些簡便方法,例如 re.match 方法
re.match(r'foo', 'foo bar')複製代碼
等價於
pattern = re.compile(r'foo')
pattern.match('foo bar')複製代碼
那麼,後者有什麼好處呢?爲了提升正則匹配的速度,它能夠重複利用正則對象,若是一個正則表達式須要匹配多個字符串,那麼就推薦後者,先編譯在去匹配。更多使用方式能夠參考文檔 docs.python.org/3/library/r…