【轉載】Python正則表示式詳解
原文地址:http://blog.csdn.net/whycadi/archive/2008/01/02/2011046.aspx
1. Python 正則式的基本用法
1.1 基本規則
1.2 重複
1.2.1 最小匹配與精確匹配
1.3 前向界定與後向界定
1.4 組的基本知識
2. re 模組的基本函式
2.1 使用 compile 加速
2.2 match 和 search
2.3 finditer
2.4
3. 更深入的瞭解 re 的組與物件
3.1 編譯後的 Pattern 物件
3.2 組與 Match 物件
3.2.1 組的名字與序號
3.2.2 Match 物件的方法
4. 更多的資料
初學
Python
,對
Python
的文書處理能力有很深的印象,除了
str
物件自帶的一些方法外,就是正則表示式這個強大的模組了。但是對於初學者來說,要用好這個功能還是有點難度,我花了好長時間才摸出了點門道。由於我記性不好,很容易就忘事,所以還是寫下來比較好一些,同時也可以加深印象,整理思路。
由於我是初學,所以肯定會有些錯誤,還望高手不吝賜教,指出我的錯誤。
1 Python 正則式的基本用法
Python 的正則表示式的模組是 ‘re’, 它的基本語法規則就是指定一個字元序列,比如你要在一個字串 s=’123abc456’ 中查詢字串 ’abc’, 只要這樣寫:
>>> import re
>>> s='123abc456eabc789'
>>> re.findall(r’abc’,s)
結果就是:
['abc', 'abc']
這裡用到的函式 ”findall(rule , target [,flag] )” 是個比較直觀的函式,就是在目標字串中查詢符合規則的字串。第一個引數是規則,第二個引數是目標字串,後面還可以跟一個規則選項(選項功能將在 compile 函式的說明中詳細說明)。返回結果結果是一個列表, 中間存放的是符合規則的字串。如果沒有符合規則的字串被找到,就返回一個空 列表。
為什麼要用 r’ ..‘ 字串( raw 字串)? 由於正則式的規則也是由一個字串定義的,而在正則式中大量使用轉義字元 ’/’ ,如果不用 raw 字串,則在需要寫一個 ’/’ 的地方,你必須得寫成 ’//’, 那麼在要從目標字串中匹配一個 ’/’ 的時候,你就得寫上 4 個 ’/’ 成為 ’////’ !這當然很麻煩,也不直觀,所以一般都使用 r’’ 來定義規則字串。當然,某些情況下,可能不用 raw 字串比較好。
以上是個最簡單的例子。當然實際中這麼簡單的用法幾乎沒有意義。為了實現複雜的規則查詢, re 規定了若干語法規則。它們分為這麼幾類:
功能字元 : ‘.’ ‘*’ ‘+’ ‘|’ ‘?’ ‘^’ ‘$’ ‘/’ 等,它們有特殊的功能含義。特別是 ’/’ 字元,它是轉義引導符號,跟在它後面的字元一般有特殊的含義。
規則分界符: ‘[‘ ‘]’ ‘ ( ’ ‘ ) ’ ‘{‘ ‘}’ 等,也就是幾種括號了。
預定義轉義字符集: “/d” “/w” “/s” 等等,它們是以字元 ’/’ 開頭,後面接一個特定字元的形式,用來指示一個預定義好的含義。
其它特殊功能字元: ’#’ ‘!’ ‘:’ ‘-‘ 等,它們只在特定的情況下表示特殊的含義,比如 (?# …) 就表示一個註釋,裡面的內容會被忽略。
下面來一個一個的說明這些規則的含義,不過說明的順序並不是按照上面的順序來的,而是我認為由淺入深,由基本到複雜的順序來編排的。同時為了直觀,在說明的過程中儘量多舉些例子以方便理解。
1.1 基本規則
‘[‘ ‘]’ 字元集合設定符
首先說明一下字元集合設定的方法。由一對方括號括起來的字元,表明一個字元集合,能夠匹配包含在其中的任意一個字元。比如 [abc123] ,表明字元 ’a’ ‘b’ ‘c’ ‘1’ ‘2’ ‘3’ 都符合它的要求。可以被匹配。
在 ’[‘ ‘]’ 中還可以通過 ’-‘ 減號來指定一個字元集合的範圍,比如可以用 [a-zA-Z] 來指定所以英文字母的大小寫,因為英文字母是按照從小到大的順序來排的。你不可以把大小的順序顛倒了,比如寫成 [z-a] 就不對了。
如果在 ’[‘ ‘]’ 裡面的開頭寫一個 ‘^’ 號,則表示取非,即在括號裡的字元都不匹配。如 [^a-zA-Z] 表明不匹配所有英文字母。但是如果 ‘^’ 不在開頭,則它就不再是表示取非,而表示其本身,如 [a-z^A-Z] 表明匹配所有的英文字母和字元 ’^’ 。
‘|’ 或規則
將兩個規則並列起來,以‘ | ’連線,表示只要滿足其中之一就可以匹配。比如
[a-zA-Z]|[0-9] 表示滿足數字或字母就可以匹配,這個規則等價於 [a-zA-Z0-9]
注意 :關於 ’|’ 要注意兩點:
第一, 它在 ’[‘ ‘]’ 之中不再表示或,而表示他本身的字元。如果要在 ’[‘ ‘]’ 外面表示一個 ’|’ 字元,必須用反斜槓引導,即 ’/|’ ;
第二, 它的有效範圍是它兩邊的整條規則,比如‘ dog|cat’ 匹配的是‘ dog’ 和 ’cat’ ,而不是 ’g’ 和 ’c’ 。如果想限定它的有效範圍,必需使用一個無捕獲組 ‘(?: )’ 包起來。比如要匹配 ‘ I have a dog’ 或 ’I have a cat’ ,需要寫成 r’I have a (?:dog|cat)’ ,而不能寫成 r’I have a dog|cat’
例
>>> s = ‘I have a dog , I have a cat’
>>> re.findall( r’I have a (?:dog|cat)’ , s )
['I have a dog', 'I have a cat'] # 正如我們所要的
下面再看看不用無捕獲組會是什麼後果:
>>> re.findall( r’I have a dog|cat’ , s )
['I have a dog', 'cat'] # 它將 ’I have a dog’ 和 ’cat’ 當成兩個規則了
至於無捕獲組的使用,後面將仔細說明。這裡先跳過。
‘.’ 匹配所有字元
匹配除換行符 ’/n’ 外的所有字元。如果使用了 ’S’ 選項,匹配包括 ’/n’ 的所有字元。
例:
>>> s=’123 /n456 /n789’
>>> findall(r‘.+’,s)
['123', '456', '789']
>>> re.findall(r‘.+’ , s , re.S)
['123/n456/n789']
‘^’ 和 ’$’ 匹配字串開頭和結尾
注意 ’^’ 不能在‘ [ ] ’中,否則含意就發生變化,具體請看上面的 ’[‘ ‘]’ 說明。 在多行模式下,它們可以匹配每一行的行首和行尾。具體請看後面 compile 函式說明的 ’M’ 選項部分
‘/d’ 匹配數字
這是一個以 ’/’ 開頭的轉義字元, ’/d’ 表示匹配一個數字,即等價於 [0-9]
‘/D’ 匹配非數字
這個是上面的反集,即匹配一個非數字的字元,等價於 [^0-9] 。注意它們的大小寫。下面我們還將看到 Python 的正則規則中很多轉義字元的大小寫形式,代表互補的關係。這樣很好記。
‘/w’ 匹配字母和數字
匹配所有的英文字母和數字,即等價於 [a-zA-Z0-9] 。
‘/W’ 匹配非英文字母和數字
即 ’/w’ 的補集,等價於 [^a-zA-Z0-9] 。
‘/s’ 匹配間隔符
即匹配空格符、製表符、回車符等表示分隔意義的字元,它等價於 [ /t/r/n/f/v] 。(注意最前面有個空格 )
‘/S’ 匹配非間隔符
即間隔符的補集,等價於 [^ /t/r/n/f/v]
‘/A’ 匹配字串開頭
匹配字串的開頭。它和 ’^’ 的區別是, ’/A’ 只匹配整個字串的開頭,即使在 ’M’ 模式下,它也不會匹配其它行的很首。
‘/Z’ 匹配字串結尾
匹配字串的結尾。它和 ’$’ 的區別是, ’/Z’ 只匹配整個字串的結尾,即使在 ’M’ 模式下,它也不會匹配其它各行的行尾。
例:
>>> s= '12 34/n56 78/n90'
>>> re.findall( r'^/d+' , s , re.M ) # 匹配位於行首的數字
['12', '56', '90']
>>> re.findall( r’/A/d+’, s , re.M ) # 匹配位於字串開頭的數字
['12']
>>> re.findall( r'/d+$' , s , re.M ) # 匹配位於行尾的數字
['34', '78', '90']
>>> re.findall( r’/d+/Z’ , s , re.M ) # 匹配位於字串尾的數字
['90']
‘/b’ 匹配單詞邊界
它匹配一個單詞的邊界,比如空格等,不過它是一個‘ 0 ’長度字元,它匹配完的字串不會包括那個分界的字元。而如果用 ’/s’ 來匹配的話,則匹配出的字串中會包含那個分界符。
例:
>>> s = 'abc abcde bc bcd'
>>> re.findall( r’/bbc/b’ , s ) # 匹配一個單獨的單詞 ‘bc’ ,而當它是其它單詞的一部分的時候不匹配
['bc'] #只找到了那個單獨的 ’bc’
>>> re.findall( r’/sbc/s’ , s ) #匹配一個單獨的單詞 ‘bc’
[' bc '] # 只找到那個單獨的 ’bc’ ,不過注意前後有兩個空格,可能有點看不清楚
‘/B’ 匹配非邊界
和 ’/b’ 相反,它只匹配非邊界的字元。它同樣是個 0 長度字元。
接上例:
>>> re.findall( r’/Bbc/w+’ , s ) # 匹配包含 ’bc’ 但不以 ’bc’ 為開頭的單詞
['bcde'] # 成功匹配了 ’abcde’ 中的 ’bcde’ ,而沒有匹配 ’bcd’
‘(?:)’ 無捕獲組
當你要將一部分規則作為一個整體對它進行某些操作,比如指定其重複次數時,你需要將這部分規則用 ’(?:’ ‘)’ 把它包圍起來,而不能僅僅只用一對括號,那樣將得到絕對出人意料的結果。
例:匹配字串中重複的 ’ab’
>>> s=’ababab abbabb aabaab’
>>> re.findall( r’/b(?:ab)+/b’ , s )
['ababab']
如果僅使用一對括號,看看會是什麼結果:
>>> re.findall( r’/b(ab)+/b’ , s )
['ab']
這是因為如果只使用一對括號,那麼這就成為了一個組 (group) 。組的使用比較複雜,將在後面詳細講解。
‘(?# )’ 註釋
Python 允許你在正則表示式中寫入註釋,在 ’(?#’ ‘)’ 之間的內容將被忽略。
(?iLmsux)
編譯選項指定
Python 的正則式可以指定一些選項,這個選項可以寫在 findall 或 compile 的引數中,也可以寫在正則式裡,成為正則式的一部分。這在某些情況下會便利一些。具體的選項含義請看後面的 compile 函式的說明。
此處編譯選項 ’i’ 等價於 IGNORECASE ,L 等價於 LOCAL ,m 等價於 MULTILINE , s 等價於 DOTALL , u 等價於 UNICODE , x 等價於 VERBOSE 。
請注意它們的大小寫。在使用時可以只指定一部分,比如只指定忽略大小寫,可寫為 ‘(?i)’ ,要同時忽略大小寫並使用多行模式,可以寫為 ‘(?im)’ 。
另外要注意選項的有效範圍是整條規則,即寫在規則的任何地方,選項都會對全部整條正則式有效。
1.2 重複
正則式需要匹配不定長的字串,那就一定需要表示重複的指示符。 Python 的正則式表示重複的功能很豐富靈活。重複規則的一般的形式是在一條字元規則後面緊跟一個表示重複次數的規則,已表明需要重複前面的規則一定的次數。重複規則有:
‘*’ 0 或多次匹配
表示匹配前面的規則 0 次或多次。
‘+’ 1 次或多次匹配
表示匹配前面的規則至少 1 次,可以多次匹配
例:匹配以下字串中的前一部分是字母,後一部分是數字或沒有的變數名字
>>> s = ‘ aaa bbb111 cc22cc 33dd ‘
>>> re.findall( r’/b[a-z]+/d*/b’ , s ) # 必須至少 1 個字母開頭,以連續數字結尾或沒有數字
['aaa', 'bbb111']
注意上例中規則前後加了表示單詞邊界的 ’/b’ 指示符,如果不加的話結果就會變成:
>>> re.findall( r’[a-z]+/d*’ , s )
['aaa', 'bbb111', 'cc22', 'cc', 'dd'] # 把單詞給拆開了
大多數情況下這不是我們期望的結果。
‘?’ 0 或 1 次匹配
只匹配前面的規則 0 次或 1 次。
例,匹配一個數字,這個數字可以是一個整數,也可以是一個科學計數法記錄的數字,比如 123 和 10e3 都是正確的數字。
>>> s = ‘ 123 10e3 20e4e4 30ee5 ‘
>>> re.findall( r’ /b/d+[eE]?/d*/b’ , s )
['123', '10e3']
它正確匹配了 123 和 10e3, 正是我們期望的。注意前後的 ’/b’ 的使用,否則將得到不期望的結果。
1.2.1 精確匹配和最小匹配
Python 正則式還可以精確指定匹配的次數。指定的方式是
‘{m}’ 精確匹配 m 次
‘{m,n}’ 匹配最少 m 次,最多 n 次。 (n>m)
如果你只想指定一個最少次數或只指定一個最多次數,你可以把另外一個引數空起來。比如你想指定最少 3 次,可以寫成 {3,} (注意那個逗號),同樣如果只想指定最大為 5 次,可以寫成 { , 5} ,也可以寫成 {0,5} 。
例 尋找下面字串中
a : 3 位數
b: 2 位數到 4 位數
c: 5 位數以上的數
d: 4 位數以下的數
>>> s= ‘ 1 22 333 4444 55555 666666 ‘
>>> re.findall( r’/b/d{3}/b’ , s ) # a : 3 位數
['333']
>>> re.findall( r’/b/d{2,4}/b’ , s ) # b: 2 位數到 4 位數
['22', '333', '4444']
>>> re.findall( r’/b/d{5,}/b’, s ) # c: 5 位數以上的數
['55555', '666666']
>>> re.findall( r’/b/d{1,4}/b’ , s ) # 4 位數以下的數
['1', '22', '333', '4444']