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

注:本文翻譯自 Regular Expression HOWTO,小甲魚童鞋對此作了一些註釋和修改。


正則表達式介紹

正則表達式(Regular expressions 也稱爲 REs,或 regexes 或 regex patterns)本質上是一個微小的且高度專業化的編程語言。它被嵌入到 Python 中,並經過 re 模塊提供給程序猿使用。使用正則表達式,你須要指定一些規則來描述那些你但願匹配的字符串集合。這些字符串集合可能包含英語句子、 e-mail 地址、TeX 命令,或任何你想要的東東。

正則表達式模式被編譯成一系列的字節碼,而後由一個 C 語言寫的匹配引擎所執行。對於高級的使用,你可能須要更關注匹配引擎是如何執行給定的 RE,並經過必定的方式來編寫 RE,以便產生一個能夠運行得更快的字節碼。本文暫不講解優化的細節,由於這須要你對匹配引擎的內部機制有一個很好的理解。但本文的例子均是符合標準的正則表達式語法。

小甲魚註釋:Python 的正則表達式引擎是用 C 語言寫的,因此效率是極高的。另,所謂的正則表達式,這裏說的 RE,就是上文咱們提到的「一些規則」。

正則表達式語言相對較小,而且受到限制,因此不是全部可能的字符串處理任務均可以使用正則表達式來完成。還有一些特殊的任務,可使用正則表達式來完成,可是表達式會所以而變得很是複雜。在這種狀況下,你可能經過本身編寫 Python 代碼來處理會更好些;儘管 Python 代碼比一個精巧的正則表達式執行起來會慢一些,但可能會更容易理解。

小甲魚註釋:這多是你們常說的「醜話說在前」吧,你們別管他,正則表達式很是優秀,她能夠處理你 98.3% 的文本任務,必定要好好學哦~~~~~


簡單的模式

咱們將從最簡單的正則表達式學習開始。因爲正則表達式經常使用於操做字符串的,所以咱們從最多見的任務下手:字符匹配。


字符匹配

大多數字母和字符會匹配它們自身。舉個例子,正則表達式 FishC 將徹底匹配字符串 "FishC"。(你能夠啓用不區分大小寫模式,這將使得 FishC 能夠匹配 "FISHC" 或 "fishc",咱們會在後邊討論這個話題。)

固然這個規則也有例外。有少數特殊的字符咱們稱之爲元字符(metacharacter),它們並不能匹配自身,它們定義了字符類、子組匹配和模式重複次數等。本文用很大的篇幅專門討論了各類元字符及其做用。

下邊是元字符的完整列表(咱們將在後邊逐一講解):

.   ^   $   *   +   ?   { }   [ ]   \   |   ( )

小甲魚註釋:若是沒有這些元字符,正則表達式就變得跟字符串的 find() 方法同樣平庸了......


咱們先來看下方括號 [ ],它們指定一個字符類用於存放你須要匹配的字符集合。能夠單獨列出須要匹配的字符,也能夠經過兩個字符和一個橫杆 - 指定匹配的範圍。例如 [abc] 會匹配字符 ab 或 c[a-c] 能夠實現相同的功能。後者使用範圍來表示與前者相同的字符集合。若是你想只匹配小寫字母,你的 RE 能夠寫成 [a-z]

須要注意的一點是:元字符在方括號中不會觸發「特殊功能」,在字符類中,它們只匹配自身。例如 [akm$] 會匹配任何字符 'a''k''m' 或 '$''$' 是一個元字符,但在方括號中它不表示特殊含義,它只匹配 '$' 字符自己。

你還能夠匹配方括號中未列出的全部其餘字符。作法是在類的開頭添加一個脫字符號 ^ ,例如 [^5] 會匹配除了 '5' 以外的任何字符。


或許最重要的元字符當屬反斜槓 \ 了。跟 Python 的字符串規則同樣,若是在反斜槓後邊緊跟着一個元字符,那麼元字符的「特殊功能」也不會被觸發。例如你須要匹配符號 [ 或 \,你能夠在它們前面加上一個反斜槓,以消除它們的特殊功能:\[\\

反斜槓後邊跟一些字符還能夠表示特殊的意義,例如表示十進制數字,表示全部的字母或者表示非空白的字符集合。

小甲魚解釋:反斜槓真牛逼,反斜槓後邊跟元字符去除特殊功能,反斜槓後邊跟普通字符實現特殊功能。

讓咱們來舉個例子:\w 匹配任何字符。若是正則表達式以字節的形式表示,這至關於字符類 [a-zA-Z0-9_];若是正則表達式是一個字符串,\w 會匹配全部 Unicode 數據庫(unicodedata 模塊提供)中標記爲字母的字符。你能夠在編譯正則表達式的時候,經過提供 re.ASCII 表示進一步限制 \w 的定義。

小甲魚解釋:re.ASCII 標誌使得 \w 只能匹配 ASCII 字符,不要忘了,Python3 是 Unicode 的。

下邊列舉一些反斜槓加字符構成的特殊含義:

html

特殊字符 含義
\d 匹配任何十進制數字;至關於類 [0-9]
\D 與 \d 相反,匹配任何非十進制數字的字符;至關於類 [^0-9]
\s 匹配任何空白字符(包含空格、換行符、製表符等);至關於類 [ \t\n\r\f\v]
\S 與 \s 相反,匹配任何非空白字符;至關於類 [^ \t\n\r\f\v]
\w 匹配任何字符,見上方解釋
\W 於 \w 相反
\b 匹配單詞的開始或結束
\B 與 \b 相反


它們能夠包含在一個字符類中,而且同樣擁有特殊含義。例如 [\s,.] 是一個字符類,它將匹配任何空白字符(/s 的特殊含義),',' 或 '.'。 

最後咱們要講的一個元字符是 .,它匹配除了換行符之外的任何字符。若是設置了 re.DOTALL 標誌,. 將匹配包括換行符在內的任何字符。


重複的事情

使用正則表達式可以輕鬆的匹配不一樣的字符集合,但 Python 字符串現有的方法卻沒法實現。然而,若是你認爲這是正則表達式的惟一優點,那你就 too young too native 了。正則表達式有另外一個強大的功能,就是你能夠指定 RE 部分被重複的次數。


咱們來看看 * 這個元字符,固然它不是匹配 '*' 字符自己(咱們說過元字符都是有特殊能力的),它用於指定前一個字符匹配零次或者屢次。

例如 ca*t 將匹配 ct(0 個字符 a),cat(1 個字符 a),caaat(3 個字符 a),等等。須要注意的是,因爲受到 C 語言的 int 類型大小的內部限制,正則表達式引擎會限制字符 'a' 的重複個數不超過 20 億個;不過,一般咱們工做中也用不到那麼大的數據。


正則表達式默認的重複規則是貪婪的,當你重複匹配一個 RE 時,匹配引擎會嘗試儘量多的去匹配。直到 RE 不匹配或者到告終尾,匹配引擎就會回退一個字符,而後再繼續嘗試匹配。

咱們經過例子一步步的給你們講解什麼叫「貪婪」:先考慮一下表達式 a[bcd]*b,首先須要匹配字符 'a',而後是零個到多個 [bcd],最後以 'b' 結尾。那如今想象一下,這個 RE 匹配字符串 abcbd 會怎樣?

python

步驟 匹配 說明
1 a 匹配 RE 的第一個字符 'a'
2 abcbd 引擎在符合規則的狀況下儘量地匹配 [bcd]*,直到該字符串的結尾
3 失敗 引擎嘗試匹配 RE 最後一個字符 'b',但當前位置已是字符串的結尾,因此失敗了結
4 abcb 回退,因此 [bcd]* 匹配少一個字符
5 失敗 再一次嘗試匹配 RE 最後一個字符 'b',但字符串最後一個字符是 'd',因此失敗了結
6 abc 再次回退,因此 [bcd]* 此次只匹配 'bc'
7 abcb 再一次嘗試匹配字符 'b',這一次字符串當前位置指向的字符正好是 'b',匹配成功


最終,RE 匹配的結果是 abcb

小甲魚解釋:正則表達式默認的匹配規則是貪婪的,後邊有教你如何使用非貪婪的方法匹配。


另外一個實現重複的元字符是 +,用於指定前一個字符匹配一次或者屢次。

要特別注意 * 和 + 的區別:* 匹配的是零次或者屢次,因此被重複的內容可能壓根兒不會出現;+ 至少須要出現一次。例如 ca+t 會匹配 cat 和 caaat,但不會匹配 ct


還有兩個表示重複的元字符,其中一個是問號 ?,用於指定前一個字符匹配零次或者一次。你能夠這麼想,它的做用就是把某種東西標誌位可選的。例如 小?甲魚 能夠匹配 小甲魚,也能夠匹配 甲魚


最靈活的應該是元字符 {m, n}(m 和 n 都是十進制整數),上邊講到的幾個元字符均可以使用它來表達,它的含義是前一個字符必須匹配 m 次到 n 次之間。例如 a/{1, 3}b 會匹配 a/ba//b 和 a///b。但不會匹配 ab(沒有斜槓);也不會匹配a////b(斜槓超過三個)。

你能夠省略 m 或者 n,這樣的話,引擎會假定一個合理的值代替。省略 m,將被解釋爲下限 0;省略 n 則會被解釋爲無窮大(事實上是上邊咱們提到的 20 億)。

小甲魚解釋:若是是 {, n} 至關於 {0, n};若是是 {m, } 至關於 {m, +無窮};若是是 {n},則是重複前一個字符 n 次。


聰明的魚油應該已經發現了,其實 *+ 和 ? 均可以使用 {m, n} 來代替。{0,} 跟 * 是同樣的;{1, } 跟 + 是同樣的;{0, 1}跟 ? 是同樣的。不過仍是鼓勵你們記住並使用 *+ 和 ?,由於這些字符更短而且更容易閱讀。

小甲魚解釋:還有一個緣由是匹配引擎對 * + ? 作了優化,效率要更高些。



正則表達式

相關文章
相關標籤/搜索