Python3 如何優雅地使用正則表達式(詳解二)

使用正則表達式

如今咱們開始來寫一些簡單的正則表達式吧。Python 經過 re 模塊爲正則表達式引擎提供一個接口,同時容許你將正則表達式編譯成模式對象,並用它們來進行匹配。

小甲魚解釋:re 模塊是使用 C 語言編寫,因此效率比你用普通的字符串方法要高得多;將正則表達式進行編譯(compile)也是爲了進一步提升效率;後邊咱們會常常提到「模式」,指的就是正則表達式被編譯成的模式對象。


編譯正則表達式

正則表達式被編譯爲模式對象,該對象擁有各類方法供你操做字符串,如查找模式匹配或者執行字符串替換。

html

  1. >>> import re
  2. >>> p = re.compile('ab*')
  3. >>> p  
  4. <_sre.SRE_Pattern object at 0x...>
複製代碼


re.compile() 也能夠接受 flags 參數,用於開啓各類特殊功能和語法變化,咱們會在後邊一一介紹。

如今咱們先來看個簡單的例子:python

  1. >>> p = re.compile('ab*', re.IGNORECASE)
複製代碼


正則表達式做爲一個字符串參數傳給 re.compile()。因爲正則表達式並非 Python 的核心部分,所以沒有爲它提供特殊的語法支持,因此正則表達式只能以字符串的形式表示。(有些應用根本就不須要使用到正則表達式,因此 Python 社區的小夥伴們認爲沒有必要將其歸入 Python 的核心。)相反,re 模塊僅僅是做爲 C 的擴展模塊包含在 Python 中,就像 socket 模塊和 zlib 模塊。

使用字符串來表示正則表達式保持了 Python 簡潔的一向風格,但也所以有一些負面影響,下邊咱們就來談一談。


麻煩的反斜槓

上一篇中咱們已經提到了,正則表達式使用 '\' 字符來使得一些普通的字符擁有特殊的能力(例如 \d 表示匹配任何十進制數字),或者剝奪一些特殊字符的能力(例如 \[ 表示匹配左方括號 '[')。這會跟 Python 字符串中實現相同功能的字符發生衝突。

小甲魚解釋:挺拗口,接着看例子你就懂了~

如今的狀況是,你須要在 LaTeX 文件中使用正則表達式匹配字符串 '\section'。由於反斜槓做爲須要匹配的特殊字符,因此你須要再它前邊加多一個反斜槓來剝奪它的特殊功能。因此咱們會把正則表達式的字符寫成 '\\section'。

但不要忘了,Python 在字符串中一樣使用反斜槓來表示特殊意義。所以,若是咱們想將 '\\section' 完整地傳給 re.compile(),咱們須要再次添加兩個反斜槓......

正則表達式

匹配字符 匹配階段
\section 須要匹配的字符串
\\section 正則表達式使用 '\\' 表示匹配字符 '\'
"\\\\section" 不巧,Python 字符串也使用 '\\' 表示字符 '\'


簡而言之,爲了匹配反斜槓這個字符,咱們須要在字符串中使用四個反斜槓才行。因此,在正則表達式中頻繁地使用反斜槓,會形成反斜槓風暴,進而致使你的字符串極其難懂。

解決方法是使用 Python 的原始字符串來表示正則表達式(就是在字符串前邊加上 r,你們還記得吧...):

socket

正則字符串 原始字符串
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"


小甲魚解釋:強烈建議使用原始字符串來表達正則表達式。


實現匹配

當你將正則表達式編譯以後,你就獲得一個模式對象。那你拿他能夠用來作什麼呢?模式對象擁有不少方法和屬性,咱們下邊列舉最重要的幾個來說:

學習

方法 功能
match() 判斷一個正則表達式是否從開始處匹配一個字符串
search() 遍歷字符串,找到正則表達式匹配的第一個位置
findall() 遍歷字符串,找到正則表達式匹配的全部位置,並以列表的形式返回
finditer() 遍歷字符串,找到正則表達式匹配的全部位置,並以迭代器的形式返回


若是沒有找到任何匹配的話,match() 和 search() 會返回 None;若是匹配成功,則會返回一個匹配對象(match object),包含全部匹配的信息:例如從哪兒開始,到哪兒結束,匹配的子字符串等等。


接下來咱們一步步講解:

spa

  1. >>> import re
  2. >>> p = re.compile('[a-z]+')
  3. >>> p
  4. re.compile('[a-z]+')
複製代碼


如今,你能夠嘗試使用正則表達式 [a-z]+ 去匹配各類字符串。

例如:code

  1. >>> p.match("")
  2. >>> print(p.match(""))
  3. None
複製代碼


由於 + 表示匹配一次或者屢次,因此空字符串不能被匹配。所以,match() 返回 None。

咱們再嘗試一個能夠匹配的字符串:htm

  1. >>> m = p.match('fishc')
  2. >>> m  
  3. <_sre.SRE_Match object; span=(0, 5), match='fishc'>
複製代碼


在這個例子中,match() 返回一個匹配對象,咱們將其存放在變量 m 中,以便往後使用。


接下來讓咱們來看看匹配對象裏邊有哪些信息吧。匹配對象包含了不少方法和屬性,如下幾個是最重要的:

對象

方法 功能
group() 返回匹配的字符串
start() 返回匹配的開始位置
end() 返回匹配的結束位置
span() 返回一個元組表示匹配位置(開始,結束)


你們看:blog

  1. >>> m.group()
  2. 'fishc'
  3. >>> m.start()
  4. 0
  5. >>> m.end()
  6. 5
  7. >>> m.span()
  8. (0, 5)
複製代碼


因爲 match() 只檢查正則表達式是否在字符串的起始位置匹配,因此 start() 老是返回 0。

然而,search() 方法可就不同咯:

  1. >>> print(p.match('^_^fishc'))
  2. None
  3. >>> m = p.search('^_^fishc')
  4. >>> print(m)
  5. <_sre.SRE_Match object; span=(3, 8), match='fishc'>
  6. >>> m.group()
  7. 'fishc'
  8. >>> m.span()
  9. (3, 8)
複製代碼


在實際應用中,最經常使用的方式是將匹配對象存放在一個局部變量中,並檢查其返回值是否爲 None。

形式一般以下:

  1. p = re.compile( ... )
  2. m = p.match( 'string goes here' )
  3. if m:
  4.     print('Match found: ', m.group())
  5. else:
  6.     print('No match')
複製代碼


有兩個方法能夠返回全部的匹配結果,一個是 findall(),另外一個是 finditer()。

findall() 返回的是一個列表:

  1. >>> p = re.compile('\d+')
  2. >>> p.findall('3只小甲魚,15條腿,多出的3條在哪裏?')
  3. ['3', '15', '3']
複製代碼


findall() 須要在返回前先建立一個列表,而 finditer() 則是將匹配對象做爲一個迭代器返回:

  1. >>> iterator = p.finditer('3只小甲魚,15條腿,還有3條去了哪裏?')
  2. >>> iterator
  3. <callable_iterator object at 0x10511b588>
  4. >>> for match in iterator:
  5.         print(match.span())
  6.         
  7. (0, 1)
  8. (6, 8)
  9. (13, 14)
複製代碼


小甲魚解釋:若是列表很大,那麼返回迭代器的效率要高不少。迭代器的相關知識請看:《零基礎入門學習Python》048 | 魔法方法:迭代器

相關文章
相關標籤/搜索