轉載:Python之正則表達式

(本文大部分內容來源於此鏈接的文章,我對部分內容做了一些改動,方便自己日後複習,若有侵權,請聯繫我,我會將此文刪除,謝謝)


import re  # 導入正則表達式模塊

re.match()  # 功能是,從開始位置匹配,若開頭不匹配,則返回None
re.search() # 在整個字符串搜索匹配
re.findall() # 在整個字符串搜索匹配,返回值爲list

注意,r’pattern’的r表示原生字符(英文爲raw),表示不會對pattern字符串中的所有字符進行任何轉義。

正則表達式中,可使用可選標誌修飾符來控制匹配模式。想使用多個標誌可通過|來指定,如,re.I | re.M,被設置爲I 和 M 標誌。

修飾符 描述
re.I 使匹配對大小寫不敏感
re.L 做本地化識別(locale-aware)匹配
re.M 多行匹配,影響 ^ 和 $
re.S 使 . 匹配包括換行在內的所有字符
re.U 根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.
re.X 該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解。

序號 部分代碼展示
1 re.search(r’[a-z]+’, ‘liuyaN1234ab9’).group() # 返回’liuya’
2 re.search(r’[a-z]+’, ‘liuyaN1234ab9’, re.I).group() #返回’liuyaN’,因爲指定了re.I,即對大小寫不敏感,大寫小寫都可以匹配成功
3 if re.match(r’[0-9]’, ‘123a’): print(‘Yeah!’) # 匹配成功,返回值Yeah!
4 re.split(r’\s+’, ‘a b c d’) # 用空格分割,返回值爲[‘a’, ‘b’, ‘c’, ‘d’]
5 re.split(r’[\s\,]+’, ‘a, b c, d’) # 返回值爲[‘a’, ‘b’, ‘c’, ‘d’]
6 re.match(r’[0-9]’, ‘3’).group() # 返回值爲’3’

模式字符串使用特殊語法來表示一個正則表達式:

  1. 字母與數字表示自己本身,一個正則表達式中的字母和數字匹配相同的字符串。
  2. 標點符號只有被轉義時才匹配自身,否則表示特殊含義。
  3. 由於大部分的正則表達式都包含反斜槓\,故最好使用原始字符串來表示,即r’string’。

下表爲正則表達式語法中的特殊元素:

模式 描述
^ 匹配字符串的開頭
$ 匹配字符串的末尾
. 匹配除換行符外的任意字符
[…] 用來表示一組字符,如,[abc]匹配’a’, ‘b’, ‘c’
[^…] 匹配不在[]中的字符,如[^abc]匹配除a,b,c之外的字符
re* 匹配0個或多個的表達式
re+ 匹配1個或多個的表達式
re? 匹配0個或1個由前面的正則表達式定義的片段,非貪婪方式
re{n} 精確匹配n個前面的表達式
re{n,} 匹配前面的表達式至少n次
re{n,m} 匹配前面的表達式至少n次,最多m次
a|b 匹配a或b
\w 匹配字母數字下劃線
\W 匹配非字母數字下劃線的字符
\s 匹配任意空白字符,等價於[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意數字,等價於[0-9]
\D 匹配任意非數字
\A 匹配字符串開始
\Z 匹配字符串結束。若存在換行,只匹配到換行前的結束字符串
\z 匹配字符串結束
\G 匹配最後匹配完成的位置
\b 匹配一個單詞邊界,即單詞與空格間的位置,如,'er\b’可以匹配‘never’的‘er’,但不能匹配‘verb’的‘er’
\B 匹配非單詞邊界,如,'er\B’可以匹配‘verb’的‘er’,但不能匹配‘never’的‘er’

正則表達式實例

1 字符匹配

正則表達式 功能
python 匹配python

2 字符類

正則表達式 功能
[Pp]ython 匹配’Python’或’python’
[aeiou] 匹配aeiou中的任意字母
[0-9] 匹配任意數字
[a-z] 匹配任何小寫字母
[A-Z] 匹配任何大寫字母
[a-zA-Z0-9] 匹配任意數字及字母
[^aeiou] 匹配除aeiou外的任意字母
[^0-9] 匹配除數字外的字符

3 特殊字符類

正則表達式 功能
. 匹配除‘\n’之外的任意單個字符。若要匹配包括‘\n’在內的任何字符,可用‘[.\n]’的模式
\d 匹配一個數字字符。等價於[0-9]
\D 匹配一個非數字字符。等價於[^0-9]
\s 匹配任何空白字符,等價於[\f\n\r\t\v]
\S 匹配任何非空白字符,等價於[^\f\n\r\t\v]
\w 匹配包括下劃線的任意單詞字符,等價於’[A-Za-z0-9_]’。
\W 匹配任意非單詞字符,等價於’[^A-Za-z0-9_]’。

Python的正則表達式的模塊是re。下面爲一段示例代碼,作用是在字符串s中查找子字符串’abc’:

import re

s = '123abc456abc789abc'
re.findall(r'abc', s)

運行結果爲:

['abc', 'abc', 'abc']

上面用到的函數findall(rule, target[,flag])的作用是在目標字符串中查找符合規則的子字符串。rule: 匹配規則(即正則表達式)。target: 目標字符串(在這個字符串中查找,最後找到匹配成功的子字符串),flag:代表規則選項參數,是可選的。返回值爲一個列表,元素是符合規則的字符串。若匹配失敗,返回空列表。


基本規則:

1 字符集合設定符——[]

由方括號括起來的字符,表示一個字符集合,檢驗待匹配字符串中是否包含這個字符集合中的任意一個字符。如,正則表達式[abc]表示字符’a’、‘b’、'c’都符合這個正則表達式的要求。

方括號中還能使用’-'號指定一個字符集合的範圍,如,正則表達式[a-zA-Z]代表所有英文字母的大小寫。

方括號中的開頭若有’^‘號,則表示取非。如,正則表達式[^a-zA-Z]表明匹配除英文字母外的所有字符。注意,若’^‘號不在開頭,則不再表示取非,而是隻表示本身,如[a-z^A-Z]表示匹配所有英文字母和’^'字符。

2 或’|'

將多個規則用’|'連接起來,表示只要滿足其一即可匹配。

注意,’|‘的有效範圍是它兩邊的整條規則。如,‘dog|cat’匹配的是dog和cat,而不是’g’和’c’。若想限定有效範圍,需使用一個無捕獲組’(?: )‘包起來。如,要匹配’a dog’或’a cat’,需要寫成r’a (?:dog|cat)’,而不能寫成r’a dog|cat’。

示例代碼如下:

import re

s = 'a dog, a cat'
re.findall(r'a (?:dog|cat)', s)

輸出結果爲:

['a dog', 'a cat']

下面的代碼中,re.findall爲使用無捕獲組(?: )

import re

s = 'a dog, a cat'
re.findall(r'a dog|cat', s)

輸出結果爲:

['a dog', 'cat']

從上述結果中可以看出findall函數將’a dog’ 和 ‘cat’分開,當成兩個規則了,而不是將其視爲’a dog’ 和’a cat’了。(無捕獲組將在後面說明)

3 匹配所有字符.

匹配除換行符\n外的所有字符。若使用了’S’選項,將匹配所有字符(包括換行符\n

下面爲實例代碼:

import re

s = '123\n456\n789'
re.findall(r'.+', s)  # 1
re.findall(r'.+', s, re.S)  # 2

輸出結果爲:

['123', '456', '789'] # 1,不可以匹配換行符
['123\n456\n789'] # 2,指定了re.S,可以匹配換行符

4 匹配字符串開頭^ 結尾$

注意,^在匹配開頭時不能被包含在[]中,否則含義會變爲匹配不在中括號[]內的字符。在多行模式下,^$可以匹配每一行的行首尾部(多行模式什麼的,後面會在函數compile的部分講解)。

5 匹配字符串開頭\A 與結尾\Z

\A^的區別是,前者只匹配整個字符串的開頭,即使在’re.M’模式下,也不會匹配其他行的行首。

\Z$的區別是,前者只匹配整個字符串的結尾,即使在’re.M’模式下,也不會匹配其他行的行尾。

示例代碼如下:

import re

s = '12 34\n56 78\n90'

re.findall(r'^\d+', s, re.M) # 匹配位於每行行首的數字
re.findall(r'\A\d+', s, re.M) # 匹配位於第一行行首的數字
re.findall(r'\d+$', s, re.M) # 匹配位於每行行尾的數字
re.findall(r'\d+\Z', s, re.M) # 匹配位於最後一行行尾的數字

輸出結果爲:

['12', '56', '90']
['12']
['34', '78', '90']
['90']

6 匹配單詞邊界\b和非邊界\B

\b匹配單詞邊界,是一個零長度字符,匹配完的字符串不會包含分界符。\s匹配時,匹配完的字符串會包含分界符。

實例代碼如下:

import re

s = 'abc abcde bc bcd'
re.findall(r'\bbc\b', s) # 匹配單詞'bc',而當'bc'爲其他單詞的一部分時不能匹配
re.findall(r'\sbc\s', s) # 匹配單詞'bc',前後還有空白符

輸出結果爲:

['bc']
[' bc ']  # 相較於上一結果,'bc'左右都有一個空格

\B匹配的是非邊界字符。是零長度字符。

實例代碼如下:

import re

s = 'abc abcde bc bcd'
re.findall(r'\Bbc\w+', s)  # 匹配包含bc,但是不以bc開頭的單詞

輸出結果爲:

['bcde']  # 匹配的是abcde的bcde

7 無捕獲組(?:)

當想將規則作爲一個整體對其進行操作,如,指定其重複次數時,我們需要將這部分規則用(?:)包起來,而不能僅僅使用一對括號。

下面的代碼是匹配字符串中重複的ab:

import re

s = 'ababab abbabb aabaab'
re.findall(r'\b(?:ab)+\b', s)

輸出結果爲:

['ababab']

8 註釋(?#)

當在(?#)之間填入內容時,python會把內容當成註釋,因而會在匹配時忽略這些內容。

9 編譯選項指定(?iLmsux)

Python的正則表達式能指定一些選項,這些選項可以寫在函數findall或compile的參數中,也可以寫在正則表達式中。

1、i是忽略大小寫。

2、L是表示特殊字符集(\w, \W, \b, \B, \s, \S),依賴於當前環境。

3、m是多行模式。

4、s是.並且包含換行符在內的任意字符。

5、u是表示特殊字符集(\w, \W, \b, \B, \s, \S, \d, \D),依賴於Unicode字符屬性數據庫。

6、x是爲了增加可讀性,忽略空格和#後面的註釋。


重複

Python中重複規則的一般形式是,在一條字符規則後緊跟一個表示重複次數的規則,來表示需要重複前面的規則多少次。

具體的規則如下:

1 0次或多次匹配*

*表示匹配前面的規則0或多次。

2 1次或多次匹配+

+表示匹配前面的規則至少一次。

示例代碼如下:

import re

s = 'aaa bbb111 cc22cc 33dd'
re.findall(r'\b[a-z]+\d*\b', s) # 匹配“至少以一個字母開頭,且以連續數字結尾或沒有數字的字符串片段

輸出結果爲:

['aaa', 'bbb111']

下面的代碼與上述代碼的不同之處是,下面的代碼中正則表達式的兩邊未添加\b指示符:

import re

s = 'aaa bbb111 cc22cc 33dd'
re.findall(r'[a-z]+\d*', s)

輸出結果是:

['aaa', 'bbb111', 'cc22', 'cc', 'dd']

上述結果表明,這樣的正則表達式會將單詞拆開,而在多數情況下,這不是我們期望的結果。

3 0次或1次匹配?

?表示匹配前面的規則0或1次。下面的代碼可以匹配一個數字,這個數字的類型是整數或是科學計數法記錄的數字,如123與10e3都是可以匹配的數字:

import re

s = '123 10e3 20e4e4 30ee5'
re.findall(r'\b\d+[eE]?\d*\b', s)

輸出結果爲:

['123', '10e3']

上述結果符合預期。注意:正則表達式的開頭與結尾需包含\b,否則,得到的結果將會變得出乎意料。

4 精確匹配和最小匹配

Python正則表達式還能指定匹配的次數,如下:

‘{m}’ 精確匹配m次

‘{m,n}’ 匹配至少m次,最多n次(n>m)

‘{m,}’ 匹配至少m次

示例代碼如下:

import re

s = '1 22 333 4444 55555 666666'

re.findall(r'\b\d{3}\b', s) # 尋找s中的三位數
re.findall(r'\b\d{2,4}\b', s) # 尋找s中的二位數到四位數
re.findall(r'\b\d{5,}\b', s) # 尋找s中的五位及以上的數
re.findall(r'\b\d{1,4}\b', s) # 尋找s中的四位及以下的數

輸出結果爲:

['333']
['22', '333', '4444']
['55555', '666666']
['1', '22', '333', '4444']

5 最小匹配 *?+???

*+?一般都是儘可能的匹配多個字符。但是有時我們需要的是儘可能少的匹配。如,下面的代碼是爲了匹配C語言的註釋/* part1 */ /* part2 */,使用的是最大規則:

import re

s = r'/* part1 */ code /* part2 */'
re.findall(r'\/\*.*\*\/', s) # 最大匹配規則
re.findall(r'\/\*.*?\*\/', s) # 第一和第三個*是轉義的\*,分別表示/*的*和*/的*。中間的*與.搭配成.*,表示匹配任意字符(除換行符)0次或更多次,而加上?號,成爲.*?表示儘可能匹配最少字符。

輸出結果爲:

['/* part1 */ code /* part2 */']
['/* part1 */', '/* part2 */']

從上述結果中可以看出第二個findall函數的返回結果是符合預期的,它只返回了註釋行。而第一個還返回了代碼部分。因此在此種情況下(指的是要求只返回註釋而不包含代碼時),應採取第二種正則表達式形式。


前向界定和後向界定

有時,我們需要匹配一個跟在特定內容後面的或前面的字符串。Python提供了一個簡便的前向界定和後向界定功能,如下:

1 前向界定(?<=...)

...代表希望匹配的字符串的前面應該出現的字符串。

2 後向界定(?=...)

...代表希望匹配的字符串的後面應該出現的字符串。

這是一個例子:我們希望找到C語言的註釋中的內容,而註釋是包含在/**/中間的,可我們又不希望匹配時把/**/也包括進來。針對這個要求,示例代碼如下:

import re

s = r'/* comment1 */ code /* comment2 */'
re.findall(r'(?<=\/\*).+?(?=\*\/)', s)

輸出結果爲:

[' comment1 ', ' comment2 ']

注意在正則表達式中,使用了.+?最小匹配,以免把正式代碼匹配進去。

注意,前向界定括號中的表達式必須是常值,也就是說,不能在前向界定的括號裏寫正則表達式。

示例代碼如下:

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'(?<=[a-z]+)\d+(?=[a-z]+)', s)

運行上述代碼。會出現大量錯誤提示信息,最後一條是:

re.error: look-behind requires fixed-width pattern

上面的代碼本意是爲了匹配符合這樣格式的數字:即數字兩邊都是字母時,匹配成功。但是Python要求:前向界定括號中的表達式必須是常值,即不能在前向界定的括號裏寫正則表達式。由此,Python解釋器返回錯誤信息。

爲了匹配【數字兩邊都是字母的數字】,可以這樣做:

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'[a-z]+(\d+)[a-z]+', s)

輸出結果爲:

['111']

當想找到【數字後面有字母的數字】時,可以在後向界定寫正則表達式:

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'\d+(?=[a-z]+)', s)

輸出結果爲:

['111', '333']

3 前向非界定(?<!...)

當我們希望的字符串前面不是...的內容時,才匹配這個字符串。

4 後向非界定(?!...)

當我們希望的字符串後面不跟着...的內容時,才匹配這個字符串。

下面的代碼是爲了【匹配後面不跟着字母的數字】:

import re

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'\d+(?!\w+)', s)

輸出結果爲:

['222']

需要注意的是,使用的不是[a-z]而是\w,下面的代碼將\w改爲[a-z],讓我們來看看會發生什麼:

re.findall(r'\d+(?![a-z]+)', s)

輸出結果爲:

['11', '222', '33']

有點尷尬,和預期不符。除了’222‘是符合預期的,再看看其他兩個數:因爲111與222中的前兩個數字11和22也滿足【是後面不跟着字母的數字】。(事實上我覺得還是有點繞,費點腦細胞以後纔可能懂個大概)


組的基本知識

1 無命名組
最基本的組是由一對圓括號括起來的正則表達式。比如上面匹配包夾在字母中間的數字的例子中使用的(\d+),再看看這代碼吧:

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'[a-z]+(\d+)[a-z]+', s)

輸出結果爲:

['111']

顯然,返回值僅僅包括(\d+)的內容。在這裏,111的前後的內容(均爲aaa)都匹配成功了,但是並沒有包含在輸出結果中。

除了最基本的形式,還能給組起名字。形式如下:

2 命名組 (?P<name>...)

(?P代表這是一個Python的語法擴展。而<name>裏面的name是給這個組起的名字,如將一個全部由數字組成的組叫做num,形式爲(?P<name>\d+)。在起名以後,就能在後面的正則表達式中通過名字調用這個組。調用已匹配的命名組的格式是(?P=name)。要注意,再次調用的這個組是已被匹配的組,即它的內容和命名組的內容是一樣的。

示例代碼如下:

import re

s = 'aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'

re.findall(r'([a-z]+)\d+([a-z]+)',s) # 匹配中間夾有數字的字母(只匹配字母,不匹配數字)
re.findall(r'(?P<g1>[a-z]+)\d+(?P=g1)',s) # 匹配中間夾有數字的字母,且前後字母要一致
re.findall(r'[a-z]+(\d+)([a-z]+)',s) # 先找出這種字符串:<字母><數字><字母>,然後匹配<數字><字母>

輸出結果爲:

[('aaa', 'aaa'), ('fff', 'ggg')]
['aaa']
[('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')]

re 模塊的基本函數

1 findall函數

語法:findall(rule, target[, flag])。
功能:在目標字符串中查找符合規則的字符串。
參數:rule爲規則(正則表達式);target爲目標字符串;flag是可選規則。
返回值:返回結果是列表類型的,存放的是符合規則的字符串。若未找到符合規則的字符串,即匹配失敗時,返回空列表[]

2 compile函數

*語法:compile(rule[, flag])
功能:將正則表達式編譯成一個Pattern對象,以供後面使用。
參數:rule爲規則(正則表達式);flag是可選規則。
返回值:返回一個Pattern對象

直接使用findall()函數來匹配字符串時,如果多次使用的話,效率會很低。這是因爲:正則引擎每次都需要把正則表達式解釋一遍,這很耗費時間,因此這樣的效率很低。若要多次使用同一規則時,可用compile函數將規則預編譯,而後再使用編譯過返回的pattern對象來進行匹配。

示例代碼如下:

import re

s = '111,222,aaa,bbb,ccc333,444ddd'
compiled_rule = re.compile(r'\b\d+\b') # 匹配單獨的數字
compiled_rule.findall(s)

輸出結果爲:

['111', '222']

compile函數還可以指定規則標誌,多個選項之間用|(位或)連接。

I ignorance:忽略大小寫。

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)

rcm = re.compile(r'^\d+', re.M) # 使用M選項
rcm.findall(s)

輸出結果爲:

['123']
['123', '789', '345']

同樣,對於$,若沒有M選項,正則表達式將只匹配最後一個行尾的數字,即678;加上M選項以後,將匹配三個行尾的數字456012678。示例代碼如下:

s = '123 456\n789 012\n345 678'

rc = re.compile(r'\d+$')
rc.findall(s)

rcm = re.compile(r'\d+$', re.M)
rcm.findall(s)

輸出結果爲:

['678']
['456', '012', '678']

S dotall.默認匹配(除換行符\n外的)所有字符,使用S選項後,就可以匹配(包括換行符\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"""
# 正則表達式的開始
\d+ # 數字
|# 規則'位或'
[a-zA-Z]+ # 單詞
""", re.VERBOSE)

可以體會出,使用了verbose選項,且對正則表達式進行一定的編排和加註釋以後,使其更易於理解。

3 match()與search()函數

語法:match(rule, targetString[, flag])
search(rule, targetString[, flag])
注意:【re的match與search函數】同【compile編譯過的pattern對象的match與search函數】的參數是不一樣的。後者更強大,是最常用的形式。
參數:rule爲規則(正則式),targetString是目標字符串,第三個爲可選選項
功能:在目標字符串中按照規則匹配得到指定格式的子字符串。
返回值:匹配成功則返回匹配對象,失敗則無返回值。返回值不是一個字符串列表,而是一個MatchObject(如果匹配成功的話),通過操作這個MatchObject,可得到更多有用信息。

需要注意的是,若匹配失敗,兩個函數均會返回一個NoneType。所以在對匹配結果進行操作時,需事先判斷是否匹配成功,如:

m = re.match(rule, targetString)

if m:
	dosomething # 需要先判斷匹配成功與否

match()與research()的唯一區別是:前者從目標字符串的開頭開始匹配,若未在開頭位置匹配成功,則匹配失敗;而後者未在開頭匹配成功時,會跳過開頭,繼續往後尋找是否可以匹配的字符串片段。在需求不一樣的情況下,根據匹配方式來選擇是使用match還是research。

示例代碼如下:

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'】

4 finditer()函數

語法:finditer(rule, targetString[, flag])
參數:rule爲規則(正則式),targetString是目標字符串,第三個爲可選選項。
返回值:返回一個迭代器。

finditer和findall的區別是:前者返回所有匹配的字符串,並存爲一個列表,而後者並不會直接返回匹配成功的字符串,而是返回一個迭代器。

示例代碼如下:

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()的形式,可按順序依次得到匹配成功返回的MatchObject。這在【對每次返回的對象進行復雜操作】時非常有用。

5 sub()與subn()

語法:sub(rule, replace, targetString[, count])
subn(rule, replace, targetString[, count])
參數:rule是正則式,replace是用來將匹配的結果替換成爲的字符串,targetString是目標字符串,count爲可選參數,意爲最多替換次數。
功能:在目標字符串中根據正則式查找可以匹配的字符串,再把匹配成功的字符串替換爲指定的字符串。可以指定最多替換次數,若沒指明次數則默認替換所有匹配成功的字符串。

這兩個函數的唯一區別是返回值:sub返回一個替換以後的字符串;subn返回一個元組,第一個元素是替換以後的字符串,第二個元素是【表明產生了多少次替換】的一個數字。

如,下面代碼的功能是將字符串中的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)

6 split()切片函數

語法:split(rule, targetString[, maxsplit])
參數:rule是正則式,targetString是目標字符串,maxsplit是最多切片次數。
功能:使用正則式在目標字符串中查找匹配的字符串,並且將這些匹配成功的字符串作爲分界,將目標字符串進行切片。
返回值:返回一個【已經被切片】以後的字符串的列表。

正則模塊中的split()與str對象的split()較爲相似。下面的代碼是想用,把字符串s分割開,同時還要求去掉逗號前後的空格:

s = 'I am fine , you are ok , he is awesome'
re.split('\s*,\s*', s)

輸出結果如下:

['I am fine', 'you are ok', 'he is awesome']

上述輸出符合預期想要的結果

7 escape(string)

這個函數可以將字符串中所有可能被解釋爲正則運算符的字符進行轉義,簡而言之就是將字符串中的【特殊字符(非字母數字)】轉義。

示例代碼如下:

s = '[a-z]+'

rule = re.escape(s)
print(rule)

輸出結果爲:

\[a\-z\]\+

更深入的瞭解re的組與對象

1 編譯後的pattern對象

用compile編譯正則式,不僅是爲了提高匹配速度,而且利用【編譯過後生成的pattern對象】可以進行其他工作。pattern對象也有findall、match、search、finditer、sub、subn、split這些函數,區別是參數不同。通常,re模塊函數的第一個參數,即正則式不再需要了,因爲規則已包含在pattern對象中;也不需要編譯選項,因爲pattern就是由正則式編譯過來的。

其中四個函數的語法規則如下:

findall(targetString[, startPos[, endPos]])
finditer(targetString[, startPos[, endPos]])
match(targetString[, startPos[, endPos]])
search(targetString[, startPos[, endPos]])

參數:targetString是目標字符串,startPos爲查找開始位置,endPos爲查找結束位置(後面這兩個參數意味着我們可以指定查找的區間,除去不感興趣的區間)。

2 組與Match對象

組與Match對象是Python正則式的重點。

2.1 組的名字與序號

正則式的每個組都有序號,序號是按定義時從左到右的順序從1開始編號。0號組就是正則式匹配目標字符串之後的結果。

示例代碼如下:

p = re.compile(r'(?P<name>[a-z]+)\s+(?P<age>\d+)\s+(?P<tel>\d+).*', re.I)
p.groupindex # 輸出【mappingproxy({'name': 1, 'age': 2, 'tel': 3})】

s = 'xw 23 123456 <++'
m = p.search(s) # 返回【<re.Match object; span=(0, 16), match='xw 23 123456 <++'>】
m.groups()  # 查看匹配的各組的情況,返回【('xw', '23', '123456')】
m.group('name') # 返回【'xw'】
m.group(1) # 使用組序號來獲取匹配的字符串,返回值爲【'xw'】
m.group(0) # 第0組爲正則式匹配目標字符串之後的結果,返回值爲【'xw 23 123456 <++'】。注意,m.group()與m.group(0)的效果是一樣的

2.2 Match對象的方法

方法 描述(下面三個方法都是通過【Match對象.方法】來調用,如m.groups())
group([index|id]) 獲取匹配字符串以後的對應索引的組,未指定參數時返回0組,即返回正則式匹配目標字符串之後的結果
groups() 返回全部的組,是元組類型
groupdict() 返回以組名爲key,匹配的內容爲values的字典

下面的代碼是上一段代碼的延續:

m.groupdict()  # 返回以組名爲key,匹配的內容爲values的字典,返回值爲【{'name': 'xw', 'age': '23', 'tel': '123456'}】
方法 描述(這三個方法都是通過【Match對象.方法】來調用),如m.start(…);而括號裏面的內容...,可以是【組名】或者【組的序號】
start() 獲取匹配的組的開始位置
end() 獲取匹配的組的結束位置
span() 獲取匹配的組的開始與結束位置

【expand(template)】,這也是一個Match對象的方法,是用正則表達式匹配目標字符串成功後的結果去替換模板裏對應的位置,組成一個新的字符串後返回它。在template中使用\g<index|name>\index表示一個組。

下面的代碼還是上一段代碼的延續(苦笑):

m.expand(r'name is \g<1>, age is \g<age>, tel is \3') # 返回值爲【'name is xw, age is 23, tel is 123456'】

Match對象還有這些屬性:

  • pos:搜索開始的位置參數
  • endpos:搜索結束的位置參數
  • lastindex:匹配的最後一個組的序號
  • lastgroup:匹配的最後一個組的組名
  • string:匹配的目標字符串(targetString)

示例代碼如下:

m.pos # 在上面的代碼中未指定開始和結束位置,所以缺省的開始位置是0,即返回【0】
m.endpos # 缺省的結束位置是16,即返回【16】
m.lastindex # 共3個組,最後一個組的序號自然是【3】
m.lastgroup # 匹配的最後一個組的組名未【'tel'】
m.string # 返回【'xw 23 123456 <++'】

(本文大部分內容來源於此鏈接的文章,我對部分內容做了一些改動,方便自己日後複習,若有侵權,請聯繫我,我會將此文刪除,謝謝)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章