python正則表達式匹配 模式匹配

               Python正則式的基本用法python

初學Python,對Python的文字處理能力有很深的印象,除了str對象自帶的一些方法外,就是正則表達式這個強大的模塊了。可是對於初學者來講,要用好這個功能仍是有點難度,我花了好長時間才摸出了點門道。因爲我記性很差,很容易就忘事,因此仍是寫下來比較好一些,同時也能夠加深印象,整理思路。正則表達式

因爲我是初學,因此確定會有些錯誤,還望高手不吝賜教,指出個人錯誤。網絡

1 Python正則式的基本用法函數

Python的正則表達式的模塊是 ‘re’,它的基本語法規則就是指定一個字符序列,好比你要在一個字符串s=’123abc456’ 中查找字符串 ’abc’,只要這樣寫:學習

>>> import reui

>>> s='123abc456eabc789'spa

>>> re.findall(r’abc’,s).net

結果就是:翻譯

['abc', 'abc']3d

這裏用到的函數 」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  <='

更多的資料

以上基本上是把Python正則式的全面的介紹了一遍了。基本上是涵蓋了Python幫助中有關正則式的所有內容。原本是想再多舉點例子的,不過一來有點累了,二來以爲例子也舉得夠多的了,你們仍是要靠本身多用多試,才能真正體會到Python 正則式的精髓。

這篇文章只能算是個學習筆記。我自己也是個初學者,錯漏的地方不免,因此最可靠的,仍是Python自帶的幫助。以及網絡上尋找的一些資料。

 

本文轉自:http://blog.csdn.net/smilelance/article/details/6529950

相關文章
相關標籤/搜索