天天看點

超詳細Python正規表達式操作指南(re使用),一

Python 自1.5版本起增加了re 子產品,它提供 Perl 風格的正規表達式模式。Python 1.5之前版本則是通過 regex

子產品提供 Emecs 風格的模式。Emacs 風格模式可讀性稍差些,而且功能也不強,是以編寫新代碼時盡量不要再使用 regex

子產品,當然偶爾你還是可能在老代碼裡發現其蹤影。

就其本質而言,正規表達式(或

RE)是一種小型的、高度專業化的程式設計語言,(在Python中)它内嵌在Python中,并通過 re

子產品實作。使用這個小型語言,你可以為想要比對的相應字元串集指定規則;該字元串集可能包含英文語句、e-mail位址、TeX指令或任何你想搞定的東西。然後你可以問諸如“這個字元串比對該模式嗎?”或“在這個字元串中是否有部分比對該模式呢?”。你也可以使用

RE 以各種方式來修改或分割字元串。

正規表達式模式被編譯成一系列的位元組碼,然後由用 C 編寫的比對引擎執行。在進階用法中,也許還要仔細留意引擎是如何執行給定 RE ,如何以特定方式編寫 RE 以令生産的位元組碼運作速度更快。本文并不涉及優化,因為那要求你已充分掌握了比對引擎的内部機制。

正規表達式語言相對小型和受限(功能有限),是以并非所有字元串處理都能用正規表達式完成。當然也有些任務可以用正規表達式完成,不過最終表達式會變得異常複雜。碰到這些情形時,編寫

Python 代碼進行處理可能反而更好;盡管 Python 代碼比一個精巧的正規表達式要慢些,但它更易了解。

簡單模式

我們将從最簡單的正規表達式學習開始。python學習交流-719-139-688由于正規表達式常用于字元串操作,那我們就從最常見的任務:字元比對 下手。

有關正規表達式底層的計算機科學上的詳細解釋(确定性和非确定性有限自動機),你可以查閱編寫編譯器相關的任何教科書。

字元比對

大多數字母和字元一般都會和自身比對。例如,正規表達式 test 會和字元串“test”完全比對。(你也可以使用大小寫不敏感模式,它還能讓這個 RE 比對“Test”或“TEST”;稍後會有更多解釋。)

這個規則當然會有例外;有些字元比較特殊,它們和自身并不比對,而是會表明應和一些特殊的東西比對,或者它們會影響到 RE 其它部分的重複次數。本文很大篇幅專門讨論了各種元字元及其作用。

這裡有一個元字元的完整清單;其含義會在本指南馀下部分進行讨論。

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

我們首先考察的元字元是"["

"]"。它們常用來指定一個字元類别,所謂字元類别就是你想比對的一個字元集。字元可以單個列出,也可以用“-”号分隔的兩個給定字元來表示一個字元區間。例如,[abc]

将比對"a", "b", 或 "c"中的任意一個字元;也可以用區間[a-c]來表示同一字元集,和前者效果一緻。如果你隻想比對小寫字母,那幺

RE 應寫成 [a-z].

元字元在類别裡并不起作用。例如,[akm$]将比對字元"a", "k", "m", 或 "$" 中的任意一個;"$"通常用作元字元,但在字元類别裡,其特性被除去,恢複成普通字元。

你可以用補集來比對不在區間範圍内的字元。其做法是把"^"作為類别的首個字元;其它地方的"^"隻會簡單比對 "^"字元本身。例如,[^5] 将比對除 "5" 之外的任意字元。

也許最重要的元字元是反斜杠"""。

做為 Python

中的字元串字母,反斜杠後面可以加不同的字元以表示不同特殊意義。它也可以用于取消所有的元字元,這樣你就可以在模式中比對它們了。舉個例子,如果你需要比對字元

"[" 或 """,你可以在它們之前用反斜杠來取消它們的特殊意義: "[ 或 ""。

一些用 """ 開始的特殊字元所表示的預定義字元集通常是很有用的,象數字集,字母集,或其它非空字元集。下列是可用的預設特殊字元:

這樣特殊字元都可以包含在一個字元類中。如,["s,.]字元類将比對任何空白字元或","或"."。

本節最後一個元字元是 . 。它比對除了換行字元外的任何字元,在 alternate 模式(re.DOTALL)下它甚至可以比對換行。"." 通常被用于你想比對“任何字元”的地方。

重複

正規表達式第一件能做的事是能夠比對不定長的字元集,而這是其它能作用在字元串上的方法所不能做到的。 不過,如果那是正規表達式唯一的附加功能的話,那麼它們也就不那麼優秀了。它們的另一個功能就是你可以指定正規表達式的一部分的重複次數。

我們讨論的第一個重複功能的元字元是 *。* 并不比對字母字元 "*";相反,它指定前一個字元可以被比對零次或更多次,而不是隻有一次。

舉個例子,ca*t

将比對 "ct" (0 個 "a" 字元), "cat" (1 個 "a"), "caaat" (3 個 "a" 字元)等等。RE

引擎有各種來自 C 的整數類型大小的内部限制,以防止它比對超過2億個 "a"

字元;你也許沒有足夠的記憶體去建造那麼大的字元串,是以将不會累計到那個限制。

象 * 這樣地重複是“貪婪的”;當重複一個 RE 時,比對引擎會試着重複盡可能多的次數。如果模式的後面部分沒有被比對,比對引擎将退回并再次嘗試更小的重複。

一步步的示例可以使它更加清晰。讓我們考慮表達式 a[bcd]*b。它比對字母 "a",零個或更多個來自類 [bcd]中的字母,最後以 "b" 結尾。現在想一想該 RE 對字元串 "abcbd" 的比對。

RE

的結尾部分現在可以到達了,它比對 "abcb"。這證明了比對引擎一開始會盡其所能進行比對,如果沒有比對然後就逐漸退回并反複嘗試 RE

剩下來的部分。直到它退回嘗試比對 [bcd] 到零次為止,如果随後還是失敗,那麼引擎就會認為該字元串根本無法比對 RE 。

另一個重複元字元是

+,表示比對一或更多次。請注意 * 和 + 之間的不同;*比對零或更多次,是以根本就可以不出現,而 +

則要求至少出現一次。用同一個例子,ca+t 就可以比對 "cat" (1 個 "a"), "caaat" (3 個 "a"), 但不能比對

"ct"。

還有更多的限定符。問号 ? 比對一次或零次;你可以認為它用于辨別某事物是可選的。例如:home-?brew 比對 "homebrew" 或 "home-brew"。

最複雜的重複限定符是

{m,n},其中 m 和 n 是十進制整數。該限定符的意思是至少有 m 個重複,至多到 n 個重複。舉個例子,a/{1,3}b 将比對

"a/b","a//b" 和 "a///b"。它不能比對 "ab" 因為沒有斜杠,也不能比對 "a////b" ,因為有四個。

你可以忽略 m 或 n;因為會為缺失的值假設一個合理的值。忽略 m 會認為下邊界是 0,而忽略 n 的結果将是上邊界為無窮大 -- 實際上是先前我們提到的 2 兆,但這也許同無窮大一樣。

細心的讀者也許注意到其他三個限定符都可以用這樣方式來表示。 {0,} 等同于 *,{1,} 等同于 +,而{0,1}則與 ? 相同。如果可以的話,最好使用 *,+,或?。很簡單因為它們更短也再容易懂。

使用正規表達式

現在我們已經看了一些簡單的正規表達式,那麼我們實際在 Python 中是如何使用它們的呢? re 子產品提供了一個正規表達式引擎的接口,可以讓你将 REs 編譯成對象并用它們來進行比對。

編譯正規表達式

正規表達式被編譯成 `RegexObject` 執行個體,可以為不同的操作提供方法,如模式比對搜尋或字元串替換。

re.compile() 也接受可選的标志參數,常用來實作不同的特殊功能和文法變更。我們稍後将檢視所有可用的設定,但現在隻舉一個例子:

被做為一個字元串發送給 re.compile()。REs 被處理成字元串是因為正規表達式不是 Python

語言的核心部分,也沒有為它建立特定的文法。(應用程式根本就不需要 REs,是以沒必要包含它們去使語言說明變得臃腫不堪。)而 re

子產品則隻是以一個 C 擴充子產品的形式來被 Python 包含,就象 socket 或 zlib 子產品一樣。

将 REs 作為字元串以保證 Python 語言的簡潔,但這樣帶來的一個麻煩就是象下節标題所講的。

反斜杠的麻煩

在早期規定中,正規表達式用反斜杠字元 (""") 來表示特殊格式或允許使用特殊字元而不調用它的特殊用法。這就與 Python 在字元串中的那些起相同作用的相同字元産生了沖突。

讓我們舉例說明,你想寫一個 RE 以比對字元串 ""section",可能是在一個 LATEX 檔案查找。為了要在程式代碼中判斷,首先要寫出想要比對的字元串。接下來你需要在所有反斜杠和元字元前加反斜杠來取消其特殊意義。

簡單地說,為了比對一個反斜杠,不得不在 RE 字元串中寫 '\',因為正規表達式中必須是 "\",而每個反斜杠按 Python 字元串字母表示的正常必須表示成 "\"。在 REs 中反斜杠的這個重複特性會導緻大量重複的反斜杠,而且所生成的字元串也很難懂。

解決的辦法就是為正規表達式使用 Python 的 raw 字元串表示;在字元串前加個 "r" 反斜杠就不會被任何特殊方式處理,是以 r"

" 就是包含"" 和 "n" 的兩個字元,而 "

" 則是一個字元,表示一個換行。正規表達式通常在 Python 代碼中都是用這種 raw 字元串表示。