初學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']
‘*?’ ‘+?’ ‘??’最小匹配
‘*’ ‘+’ ‘?’通常都是盡可能多的匹配字符。有時候我們希望它盡可能少的匹配。比如一個c語言的注釋‘/* part 1 */ /* part 2 */’,如果使用最大規則:
>>> s =r ‘/* part 1 */ code /* part 2 */’
>>> re.findall( r’//*.*/*/’ , s )
[‘/* part 1 */ code /* part 2 */’]
結果把整個字符串都包括進去了。如果把規則改寫成
>>> re.findall( r’//*.*?/*/’ , s ) #在*后面加上?,表示盡可能少的匹配
['/* part 1 */', '/* part 2 */']
結果正確的匹配出了注釋里的內容
1.3 前向界定與后向界定
有時候需要匹配一個跟在特定內容后面的或者在特定內容前面的字符串,Python提供一個簡便的前向界定和后向界定功能,或者叫前導指定和跟從指定功能。它們是:
‘(?<=…)’前向界定
括號中’…’代表你希望匹配的字符串的前面應該出現的字符串。
‘(?=…)’ 后向界定
括號中的’…’代表你希望匹配的字符串后面應該出現的字符串。
例:你希望找出c語言的注釋中的內容,它們是包含在’/*’和’*/’之間,不過你并不希望匹配的結果把’/*’和’*/’也包括進來,那么你可以這樣用:
>>> s=r’/* comment 1 */ code /* comment 2 */’
>>> re.findall( r’(?<=//*).+?(?=/*/)’ , s )
[' comment 1 ', ' comment 2 ']
注意這里我們仍然使用了最小匹配,以避免把整個字符串給匹配進去了。
要注意的是,前向界定括號中的表達式必須是常值,也即你不可以在前向界定的括號里寫正則式。比如你如果在下面的字符串中想找到被字母夾在中間的數字,你不可以用前向界
定:
例:
>>> s = ‘aaa111aaa , bbb222 , 333ccc ‘
>>> re.findall( r’(?<=[a-z]+)/d+(?=[a-z]+)' , s ) #錯誤的用法
它會給出一個錯誤信息:
error: look-behind requires fixed-width pattern
不過如果你只要找出后面接著有字母的數字,你可以在后向界定寫正則式:
>>> re.findall( r’/d+(?=[a-z]+)’, s )
['111', '333']
如果你一定要匹配包夾在字母中間的數字,你可以使用組(group)的方式
>>> re.findall (r'[a-z]+(/d+)[a-z]+' , s )
['111']
組的使用將在后面詳細講解。
除了前向界定前向界定和后向界定外,還有前向非界定和后向非界定,它的寫法為:
‘(?<!...)’前向非界定
只有當你希望的字符串前面不是’…’的內容時才匹配
‘(?!...)’后向非界定
只有當你希望的字符串后面不跟著’…’內容時才匹配。
接上例,希望匹配后面不跟著字母的數字
>>> re.findall( r’/d+(?!/w+)’ , s )
['222']
注意這里我們使用了/w而不是像上面那樣用[a-z],因為如果這樣寫的話,結果會是:
>>> re.findall( r’/d+(?![a-z]+)’ , s )
['11', '222', '33']
這和我們期望的似乎有點不一樣。它的原因,是因為’111’和’222’中的前兩個數字也是滿足這個要求的。因此可看出,正則式的使用還是要相當小心的,因為我開始就是這樣
寫的,看到結果后才明白過來。不過Python試驗起來很方便,這也是腳本語言的一大優點,可以一步一步的試驗,快速得到結果,而不用經過煩瑣的編譯、鏈接過程。也因此學習
Python就要多試,跌跌撞撞的走過來,雖然曲折,卻也很有樂趣。
1.4組的基本知識
上面我們已經看過了Python的正則式的很多基本用法。不過如果僅僅是上面那些規則的話,還是有很多情況下會非常麻煩,比如上面在講前向界定和后向界定時,取夾在字母中間
的數字的例子。用前面講過的規則都很難達到目的,但是用了組以后就很簡單了。
‘(‘’)’ 無命名組
最基本的組是由一對圓括號括起來的正則式。比如上面匹配包夾在字母中間的數字的例子中使用的(/d+),我們再回顧一下這個例子:
>>> s = ‘aaa111aaa , bbb222 , 333ccc ‘
>>> re.findall (r'[a-z]+(/d+)[a-z]+' , s )
['111']
可以看到findall函數只返回了包含在’()’中的內容,而雖然前面和后面的內容都匹配成功了,卻并不包含在結果中。
除了最基本的形式外,我們還可以給組起個名字,它的形式是
‘(?P<name>…)’命名組
‘(?P’代表這是一個Python的語法擴展’<…>’里面是你給這個組起的名字,比如你可以給一個全部由數字組成的組叫做’num’,它的形式就是’(?P<num>/d+)’。起了名字之
后,我們就可以在后面的正則式中通過名字調用這個組,它的形式是
‘(?P=name)’調用已匹配的命名組
要注意,再次調用的這個組是已被匹配的組,也就是說它里面的內容是和前面命名組里的內容是一樣的。
我們可以看更多的例子:請注意下面這個字符串各子串的特點。
>>> s='aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'
我們看看下面的正則式會返回什么樣的結果:
>>> re.findall( r'([a-z]+)/d+([a-z]+)' , s ) #找出中間夾有數字的字母
[('aaa', 'aaa'), ('fff', 'ggg')]
>>> re.findall( r '(?P<g1>[a-z]+)/d+(?P=g1)' , s ) #找出被中間夾有數字的前后同樣的字母
['aaa']
>>> re.findall( r'[a-z]+(/d+)([a-z]+)' , s ) #找出前面有字母引導,中間是數字,后面是字母的字符串中的中間的數字和后面的字母
[('111', 'aaa'), ('777', 'ggg')]
我們可以通過命名組的名字在后面調用已匹配的命名組,不過名字也不是必需的。
‘/number’ 通過序號調用已匹配的組
正則式中的每個組都有一個序號,序號是按組從左到右,從1開始的數字,你可以通過下面的形式來調用已匹配的組
比如上面找出被中間夾有數字的前后同樣的字母的例子,也可以寫成:
>>> re.findall( r’([a-z]+)/d+/1’ , s )
['aaa']
結果是一樣的。
我們再看一個例子
>>> s='111aaa222aaa111 , 333bbb444bb33'
>>> re.findall( r'(/d+)([a-z]+)(/d+)(/2)(/1)' , s ) #找出完全對稱的數字-字母-數字-字母-數字中的數字和字母
[('111', 'aaa', '222', 'aaa', '111')]
Python2.4以后的re模塊,還加入了一個新的條件匹配功能
‘(?(id/name)yes-pattern|no-pattern)’ 判斷指定組是否已匹配,執行相應的規則
這個規則的含義是,如果id/name指定的組在前面匹配成功了,則執行yes-pattern的正則式,否則執行no-pattern的正則式。
舉個例子,比如要匹配一些形如usr@mail的郵箱地址,不過有的寫成< usr@mail >即用一對<>括起來,有點則沒有,要匹配這兩種情況,可以這樣寫
>>> s='<usr1@mail1> usr2@maill2'
>>> re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , s )
[('<', 'usr1@mail1'), ('', 'usr2@maill2')]
不過如果目標字符串如下
>>> s='<usr1@mail1> usr2@maill2 <usr3@mail3 usr4@mail4> < usr5@mail5 '
而你想得到要么由一對<>包圍起來的一個郵件地址,要么得到一個沒有被<>包圍起來的地址,但不想得到一對<>中間包圍的多個地址或不完整的<>中的地址,那么使用這個式子并
不能得到你想要的結果
>>> re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , s )
[('<', 'usr1@mail1'), ('', 'usr2@maill2'), ('', 'usr3@mail3'), ('', 'usr4@mail4'), ('', 'usr5@mail5')]
它仍然找到了所有的郵件地址。
想要實現這個功能,單純的使用findall有點吃力,需要使用其它的一些函數,比如match或search函數,再配合一些控制功能。這部分的內容將在下面詳細講解。
小結:以上基本上講述了Python正則式的語法規則。雖然大部分語法規則看上去都很簡單,可是稍不注意,仍然會得到與期望大相徑庭的結果,所以要寫好正則式,需要仔細的體
會正則式規則的含義后不同規則之間細微的差別。
詳細的了解了規則后,再配合后面就要介紹的功能函數,就能最大的發揮正則式的威力了。
2 re模塊的基本函數
在上面的說明中,我們已經對re模塊的基本函數‘findall’很熟悉了。當然如果光有findall的話,很多功能是不能實現的。下面開始介紹一下re模塊其它的常用基本函數。靈活
搭配使用這些函數,才能充分發揮Python正則式的強大功能。
首先還是說下老熟人findall函數吧
findall(rule , target [,flag] )
在目標字符串中查找符合規則的字符串。
第一個參數是規則,第二個參數是目標字符串,后面還可以跟一個規則選項(選項功能將在compile函數的說明中詳細說明)。
返回結果結果是一個列表,中間存放的是符合規則的字符串。如果沒有符合規則的字符串被找到,就返回一個空列表。
2.1使用compile加速
compile( rule [,flag] )
將正則規則編譯成一個Pattern對象,以供接下來使用。
第一個參數是規則式,第二個參數是規則選項。
返回一個Pattern對象
直接使用findall ( rule , target )的方式來匹配字符串,一次兩次沒什么,如果是多次使用的話,由于正則引擎每次都要把規則解釋一遍,而規則的解釋又是相當費時間的,
所以這樣的效率就很低了。如果要多次使用同一規則來進行匹配的話,可以使用re.compile函數來將規則預編譯,使用編譯過返回的Regular Expression Object或叫做Pattern對
象來進行查找。
例
>>> s='111,222,aaa,bbb,ccc333,444ddd'
>>> rule=r’/b/d+/b’
>>> compiled_rule=re.compile(rule)
>>> compiled_rule.findall(s)
['111', '222']
可見使用compile過的規則使用和未編譯的使用很相似。compile函數還可以指定一些規則標志,來指定一些特殊選項。多個選項之間用’|’(位或)連接起來。
I IGNORECASE 忽略大小寫區別。
L LOCAL 字符集本地化。這個功能是為了支持多語言版本的字符集使用環境的,比如在轉義符/w,在英文環境下,它代表[a-zA-Z0-9],即所以英文字符和數字。如果在一個法
語環境下使用,缺省設置下,不能匹配"é"或"ç"。加上這L選項和就可以匹配了。不過這個對于中文環境似乎沒有什么用,它仍然不能匹配中文字符。
M MULTILINE 多行匹配。在這個模式下’^’(代表字符串開頭)和’$’(代表字符串結尾)將能夠匹配多行的情況,成為行首和行尾標記。比如
>>> s=’123 456/n789 012/n345 678’
>>> rc=re.compile(r’^/d+’) #匹配一個位于開頭的數字,沒有使用M選項
>>> rc.findall(s)
['123'] #結果只能找到位于第一個行首的’123’
>>> rcm=re.compile(r’^/d+’,re.M) #使用M選項
>>> rcm.findall(s)
['123', '789', '345'] #找到了三個行首的數字
同樣,對于’$’來說,沒有使用M選項,它將匹配最后一個行尾的數字,即’678’,加上以后,就能匹配三個行尾的數字456 012和678了.
>>> rc=re.compile(r’/d+$’)
>>> rcm=re.compile(r’/d+$’,re.M)
>>> rc.findall(s)
['678']
>>> rcm.findall(s)
['456', '012', '678']
S DOTALL ‘.’號將匹配所有的字符。缺省情況下’.’匹配除換行符’/n’外的所有字符,使用這一選項以后,’.’就能匹配包括’/n’的任何字符了。
U UNICODE /w,/W,/b,/B,/d,/D,/s和/S都將使用Unicode。
X VERBOSE 這個選項忽略規則表達式中的空白,并允許使用’#’來引導一個注釋。這樣可以讓你把規則寫得更美觀些。比如你可以把規則
>>> rc = re.compile(r"/d+|[a-zA-Z]+") #匹配一個數字或者單詞
使用X選項寫成:
>>> rc = re.compile(r""" # start a rule/d+ # number| [a-zA-Z]+ # word""", re.VERBOSE)在這個模式下,如果你想匹配一個空格,你必須
用'/ '的形式('/'后面跟一個空格)
2.2 match與search
match( rule , targetString [,flag] )
search( rule , targetString [,flag] )
(注:re的match與search函數同compile過的Pattern對象的match與search函數的參數是不一樣的。Pattern對象的match與search函數更為強大,是真正最常用的函數)
按照規則在目標字符串中進行匹配。
第一個參數是正則規則,第二個是目標字符串,第三個是選項(同compile函數的選項)
返回:若成功返回一個Match對象,失敗無返回
findall雖然很直觀,但是在進行更復雜的操作時,就有些力不從心了。此時更多的使用的是match和search函數。他們的參數和findall是一樣的,都是:
match( rule , targetString [,flag] )
search( rule , targetString [,flag] )
不過它們的返回不是一個簡單的字符串列表,而是一個MatchObject(如果匹配成功的話).。通過操作這個matchObject,我們可以得到更多的信息。
需要注意的是,如果匹配不成功,它們則返回一個NoneType。所以在對匹配完的結果進行操作之前,你必需先判斷一下是否匹配成功了,比如:
>>> m=re.match( rule , target )
>>> if m: #必需先判斷是否成功
doSomethin
這兩個函數唯一的區別是:match從字符串的開頭開始匹配,如果開頭位置沒有匹配成功,就算失敗了;而search會跳過開頭,繼續向后尋找是否有匹配的字符串。針對不同的需
要,可以靈活使用這兩個函數。
關于match返回的MatchObject如果使用的問題,是Python正則式的精髓所在,它與組的使用密切相關。我將在下一部分詳細講解,這里只舉個最簡單的例子:
例:
>>> s= 'Tom:9527 , Sharry:0003'
>>> m=re.match( r'(?P<name>/w+):(?P<num>/d+)' , s )
>>> m.group()
'Tom:9527'
>>> m.groups()
('Tom', '9527')
>>> m.group(‘name’)
'Tom'
>>> m.group(‘num’)
'9527'
2.3 finditer
finditer( rule , target [,flag] )
參數同findall
返回一個迭代器
finditer函數和findall函數的區別是,findall返回所有匹配的字符串,并存為一個列表,而finditer則并不直接返回這些字符串,而是返回一個迭代器。關于迭代器,解釋起來
有點復雜,還是看看例子把:
>>> s=’111 222 333 444’
>>> for i in re.finditer(r’/d+’ , s ):
print i.group(),i.span() #打印每次得到的字符串和起始結束位置
結果是
111 (0, 3)
222 (4, 7)
333 (8, 11)
444 (12, 15)
簡單的說吧,就是finditer返回了一個可調用的對象,使用for i in finditer()的形式,可以一個一個的得到匹配返回的Match對象。這在對每次返回的對象進行比較復雜的操作
時比較有用。
2.4字符串的替換和修改
re模塊還提供了對字符串的替換和修改函數,他們比字符串對象提供的函數功能要強大一些。這幾個函數是
sub ( rule , replace , target [,count] )
subn(rule , replace , target [,count] )
在目標字符串中規格規則查找匹配的字符串,再把它們替換成指定的字符串。你可以指定一個最多替換次數,否則將替換所有的匹配到的字符串。
第一個參數是正則規則,第二個參數是指定的用來替換的字符串,第三個參數是目標字符串,第四個參數是最多替換次數。
這兩個函數的唯一區別是返回值。
sub返回一個被替換的字符串
sub返回一個元組,第一個元素是被替換的字符串,第二個元素是一個數字,表明產生了多少次替換。
例,將下面字符串中的’dog’全部替換成’cat’
>>> s=’ I have a dog , you have a dog , he have a dog ‘
>>> re.sub( r’dog’ , ‘cat’ , s )
' I have a cat , you have a cat , he have a cat '
如果我們只想替換前面兩個,則
>>> re.sub( r’dog’ , ‘cat’ , s , 2 )
' I have a cat , you have a cat , he have a dog '
或者我們想知道發生了多少次替換,則可以使用subn
>>> re.subn( r’dog’ , ‘cat’ , s )
(' I have a cat , you have a cat , he have a cat ', 3)
split( rule , target [,maxsplit] )
切片函數。使用指定的正則規則在目標字符串中查找匹配的字符串,用它們作為分界,把字符串切片。
第一個參數是正則規則,第二個參數是目標字符串,第三個參數是最多切片次數
返回一個被切完的子字符串的列表
這個函數和str對象提供的split函數很相似。舉個例子,我們想把上例中的字符串被’,’分割開,同時要去掉逗號前后的空格
>>> s=’ I have a dog , you have a dog , he have a dog ‘
>>> re.split( ‘/s*,/s*’ , s )
[' I have a dog', 'you have a dog', 'he have a dog ']
結果很好。如果使用str對象的split函數,則由于我們不知道’,’兩邊會有多少個空格,而不得不對結果再進行一次處理。
escape( string )
這是個功能比較古怪的函數,它的作用是將字符串中的non-alphanumerics字符(我已不知道該怎么翻譯比較好了)用反義字符的形式顯示出來。有時候你可能希望在正則式中匹
配一個字符串,不過里面含有很多re使用的符號,你要一個一個的修改寫法實在有點麻煩,你可以使用這個函數,
例在目標字符串s中匹配’(*+?)’這個子字符串
>>> s= ‘111 222 (*+?) 333’
>>> rule= re.escape( r’(*+?)’ )
>>> print rule
/(/*/+/?/)
>>> re.findall( rule , s )
['(*+?)']
3 更深入的了解re的組與對象
前面對Python正則式的組進行了一些簡單的介紹,由于還沒有介紹到match對象,而組又是和match對象密切相關的,所以必須將它們結合起來介紹才能充分地說明它們的用途。
不過再詳細介紹它們之前,我覺得有必要先介紹一下將規則編譯后的生成的patter對象
3.1編譯后的Pattern對象
將一個正則式,使用compile函數編譯,不僅是為了提高匹配的速度,同時還能使用一些附加的功能。編譯后的結果生成一個Pattern對象,這個對象里面有很多函數,他們看起來
和re模塊的函數非常象,它同樣有findall , match , search ,finditer , sub , subn , split這些函數,只不過它們的參數有些小小的不同。一般說來,re模塊函數的第一個
參數,即正則規則不再需要了,應為規則就包含在Pattern對象中了,編譯選項也不再需要了,因為已經被編譯過了。因此re模塊中函數的這兩個參數的位置,就被后面的參數取
代了。
findall , match , search和finditer這幾個函數的參數是一樣的,除了少了規則和選項兩個參數外,它們又加入了另外兩個參數,它們是:查找開始位置和查找結束位置,也就
是說,現在你可以指定查找的區間,除去你不感興趣的區間。它們現在的參數形式是:
findall ( targetString [, startPos [,endPos] ] )
finditer ( targetString [, startPos [,endPos] ] )
match ( targetString [, startPos [,endPos] ] )
search ( targetString [, startPos [,endPos] ] )
這些函數的使用和re模塊的同名函數使用完全一樣。所以就不多介紹了。
除了和re模塊的函數同樣的函數外,Pattern對象還多了些東西,它們是:
flags 查詢編譯時的選項
pattern查詢編譯時的規則
groupindex規則里的組
這幾個不是函數,而是一個值。它們提供你一些規則的信息。比如下面這個例子
>>> p=re.compile( r'(?P<word>/b[a-z]+/b)|(?P<num>/b/d+/b)|(?P<id>/b[a-z_]+/w*/b)' , re.I )
>>> p.flags
2
>>> p.pattern
'(?P<word>//b[a-z]+//b)|(?P<num>//b//d+//b)|(?P<id>//b[a-z_]+//w*//b)'
>>> p.groupindex
{'num': 2, 'word': 1, 'id': 3}
我們來分析一下這個例子:這個正則式是匹配單詞、或數字、或一個由字母或’_’開頭,后面接字母或數字的一個ID。我們給這三種情況的規則都包入了一個命名組,分別命名
為’word’ , ‘num’和‘id’。我們規定大小寫不敏感,所以使用了編譯選項‘I’。
編譯以后返回的對象為p,通過p.flag我們可以查看編譯時的選項,不過它顯示的不是’I’,而是一個數值2。其實re.I是一個整數,2就是它的值。我們可以查看一下:
>>> re.I
2
>>> re.L
4
>>> re.M
8
…
每個選項都是一個數值。
通過p.pattern可以查看被編譯的規則是什么。使用print的話會更好看一些
>>> print p.pattern
(?P<word>/b[a-z]+/b)|(?P<num>/b/d+/b)|(?P<id>/b[a-z_]+/w*/b)
看,和我們輸入的一樣。
接下來的p.groupindex則是一個字典,它包含了規則中的所有命名組。字典的key是名字,values是組的序號。由于字典是以名字作為key,所以一個無命名的組不會出現在這里。
3.2組與Match對象
組與Match對象是Python正則式的重點。只有掌握了組和Match對象的使用,才算是真正學會了Python正則式。
3.2.1 組的名字與序號
正則式中的每個組都有一個序號,它是按定義時從左到右的順序從1開始編號的。其實,re的正則式還有一個0號組,它就是整個正則式本身。
我們來看個例子
>>> p=re.compile( r’(?P<name>[a-z]+)/s+(?P<age>/d+)/s+(?P<tel>/d+).*’ , re.I )
>>> p.groupindex
{'age': 2, 'tel': 3, 'name': 1}
>>> s=’Tom 24 88888888 <=’
>>> m=p.search(s)
>>> m.groups() #看看匹配的各組的情況
('Tom', '24', '8888888')
>>> m.group(‘name’) #使用組名獲取匹配的字符串
‘Tom’
>>> m.group( 1 ) #使用組序號獲取匹配的字符串,同使用組名的效果一樣
>>> m.group(0) # 0組里面是什么呢?
'Tom 24 88888888 <='
原來0組就是整個正則式,包括沒有被包圍到組里面的內容。當獲取0組的時候,你可以不寫這個參數。m.group(0)和m.group()的效果是一樣的:
>>> m.group()
'Tom 24 88888888 <='
接下來看看更多的Match對象的方法,看看我們能做些什么。
3.2.2 Match對象的方法
group([index|id]) 獲取匹配的組,缺省返回組0,也就是全部值
groups() 返回全部的組
groupdict() 返回以組名為key,匹配的內容為values的字典
接上例:
>>> m.groupindex()
{'age': '24', 'tel': '88888888', 'name': 'Tom'}
start( [group] ) 獲取匹配的組的開始位置
end( [group] ) 獲取匹配的組的結束位置
span( [group] ) 獲取匹配的組的(開始,結束)位置
expand( template )根據一個模版用找到的內容替換模版里的相應位置
這個功能比較有趣,它根據一個模版來用匹配到的內容替換模版中的相應位置,組成一個新的字符串返回。它使用/g<index|name>或/index來指示一個組。
接上例
>>> m.expand(r'name is /g<1> , age is /g<age> , tel is /3')
'name is Tom , age is 24 , tel is 88888888'
除了以上這些函數外,Match對象還有些屬性
pos 搜索開始的位置參數
endpos 搜索結束的位置參數
這兩個是使用findall或match等函數時,傳入的參數。在上面這個例子里,我們沒有指定開始和結束位置,那么缺省的開始位置就是0,結束位置就是最后。
>>> m.pos
0
>>> m.endpos
19
lastindex 最后匹配的組的序號
>>> m.lastindex
3
lastgroup 最后匹配的組名
>>> m.lastgroup
'tel'
re 產生這個匹配的Pattern對象,可以認為是個逆引用
>>> m.re.pattern
'(?P<name>[a-z]+)//s+(?P<age>//d+)//s+(?P<tel>//d+).*'
得到了產生這個匹配的規則
string 匹配的目標字符串
>>> m.string
'Tom 24 88888888 <='
轉自:
http://hi.baidu.com/yangdaming1983/item/e6a8146255a5442169105b91
摘要: 1 概述1.1 什么是捕獲組捕獲組就是把正則表達式中子表達式匹配的內容,保存到內存中以數字編號或顯式命名的組里,方便后面引用。當然,這種引用既可以是在正則表達式內部,也可以是在正則表達式外部。捕獲組有兩種形式,一種是普通捕獲組,另一種是命名捕獲組,...