正則表達式是一個特殊的字符序列,它能幫助你方便的檢查一個字符串是否與某種模式匹配。
re 模塊使 Python 語言擁有全部的正則表達式功能。compile 函數根據一個模式字符串和可選的標誌參數生成一個正則表達式對象。該對象擁有一系列方法用於正則表達式匹配和替換。
正則表達式的大致匹配過程是:依次拿出表達式和文本中的字符比較,如果每一個字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。如果表達式中有量詞或邊界,這個過程會稍微有一些不同,但也是很好理解的,看下圖中的示例以及自己多使用幾次就能明白。
re模塊
Python通過re模塊提供對正則表達式的支持。使用re的一般步驟是先將正則表達式的字符串形式編譯爲Pattern實例,然後使用Pattern實例處理文本並獲得匹配結果(一個Match實例),最後使用Match實例獲得信息,進行其他的操作。
re.match 嘗試從字符串的起始位置匹配一個模式,如果不是起始位置匹配成功的話,match()就返回none。
re.match(pattern, string, flags=0)
函數參數說明:pattern匹配的正則表達式;string要匹配的字符串;flags標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等。匹配成功re.match方法返回一個匹配的對象,否則返回None。我們可以使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。
re.match #從開始位置開始匹配,如果開頭沒有則無
re.search #搜索整個字符串
re.findall #搜索整個字符串,返回一個list
r(raw)用在pattern之前,表示單引號中的字符串爲原生字符,不會進行任何轉義
re.match(r'l','liuyan1').group() #返回l
re.match(r'y','liuyan1') #返回None
re.search(r'y','liuyan1').group() #返回y
正則表達式可以包含一些可選標誌修飾符來控制匹配的模式。修飾符被指定爲一個可選的標誌。多個標誌可以通過按位 OR(|) 它們來指定。如 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 | 該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解 |
爲什麼要用 r’ ..‘ 字符串( raw 字符串)? 由於正則式的規則也是由一個字符串定義的,而在正則式中大量使用轉義字符 ’/’ ,如果不用 raw 字符串,則在需要寫一個 ’/’ 的地方,你必須得寫成 ’//’, 那麼在要從目標字符串中匹配一個 ’/’ 的時候,你就得寫上 4 個 ’/’ 成爲 ’////’ !這當然很麻煩,也不直觀,所以一般都使用 r’’ 來定義規則字符串。當然,某些情況下,可能不用 raw 字符串比較好。
re.search(r'[a-z]+','liuyaN1234ab9').group() #返回'liuya'
re.search(r'[a-z]+','liuyaN1234ab9', re.I).group() #返回'liuyaN',對大小寫不敏感
#如果匹配成功,則打印m,否則返回Null
if re.match(r'[0-9]','a'):print 'm'
#用空格分割
re.split(r'\s+', 'a b c') #返回:['a', 'b', 'c', 'd']
#用逗號分隔
re.split(r'[\s\,]+', 'a,b, c d') #返回:['a', 'b', 'c', 'd']
import re
print(re.match('www', 'www.baidu.com').span()) # 在起始位置匹配
print(re.match('com', 'www.baidu.com')) # 不在起始位置匹配
line = "Cats are smarter than dogs" matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I) if matchObj:
print "matchObj.group() : ", matchObj.group() print "matchObj.group(1) : ", matchObj.group(1) print "matchObj.group(2) : ", matchObj.group(2)
else:
print "No match!!"
運行結果:
(0, 3)
None
matchObj.group() : Cats are smarter than dogs
matchObj.group(1) : Cats
matchObj.group(2) : smarter
re.compile(strPattern[, flag])
這個方法是Pattern類的工廠方法,用於將字符串形式的正則表達式編譯爲Pattern對象。 第二個參數flag是匹配模式,取值可以使用按位或運算符'|'表示同時生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)與re.compile('(?im)pattern')是等價的。
可選值有:
re.I(re.IGNORECASE): 忽略大小寫(括號內是完整寫法,下同)
M(MULTILINE): 多行模式,改變'^'和'$'的行爲(參見上圖)
S(DOTALL): 點任意匹配模式,改變'.'的行爲
L(LOCALE): 使預定字符類 \w \W \b \B \s \S 取決於當前區域設定
U(UNICODE): 使預定字符類 \w \W \b \B \s \S \d \D 取決於unicode定義的字符屬性
X(VERBOSE): 詳細模式。這個模式下正則表達式可以是多行,忽略空白字符,並可以加入註釋。以下兩個正則表達式是等價的:
import re
pattern = re.compile(r'hello')
match = pattern.match('hello world!')
if match:
print match.group()
運行結果:
hello
(1)功能字符 : ‘.’ ‘*’ ‘+’ ‘|’ ‘?’ ‘^’ ‘$’ ‘/’ 等,它們有特殊的功能含義。特別是 ’/’ 字符,它是轉義引導符號,跟在它後面的字符一般有特殊的含義。
(2)規則分界符: ‘[‘ ‘]’ ‘ ( ’ ‘ ) ’ ‘{‘ ‘}’ 等,也就是幾種括號了。
(3)預定義轉義字符集: “/d” “/w” “/s” 等等,它們是以字符 ’/’ 開頭,後面接一個特定字符的形式,用來指示一個預定義好的含義。
(4)其它特殊功能字符: ’#’ ‘!’ ‘:’ ‘-‘ 等,它們只在特定的情況下表示特殊的含義,比如 (?# …) 就表示一個註釋,裏面的內容會被忽略。
‘[‘ ‘]’字符集合設定符
首先說明一下字符集合設定的方法。由一對方括號括起來的字符,表明一個字符集合,能夠匹配包含在其中的任意一個字符。比如 [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]。
import re
s = ‘I have a dog , I have a cat’
re.findall( r’I have a (?:dog|cat)’ , s )
re.findall( r’I have a dog|cat’ , s )
運行結果:
['I have a dog', 'I have a cat']
['I have a dog', 'cat']
(1):它在 ’[‘ ‘]’ 之中不再表示或,而表示他本身的字符。如果要在 ’[‘ ‘]’ 外面表示一個 ’|’ 字符,必須用反斜槓引導,即 ’/|’。
(2):它的有效範圍是它兩邊的整條規則,比如‘ 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’。
‘.’匹配所有字符
匹配除換行符 ’/n’ 外的所有字符。如果使用了 ’S’ 選項,匹配包括 ’/n’ 的所有字符。
import re
s=’123 /n456 /n789’
findall(r‘.+’,s)
re.findall(r‘.+’ , s , re.S)
運行結果:
['123', '456', '789']
['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’ 模式下,它也不會匹配其它各行的行尾。
‘/b’ 匹配單詞邊界
它匹配一個單詞的邊界,比如空格等,不過它是一個‘ 0 ’長度字符,它匹配完的字符串不會包括那個分界的字符。而如果用 ’/s’ 來匹配的話,則匹配出的字符串中會包含那個分界符。
‘/B’ 匹配非邊界
和 ’/b’ 相反,它只匹配非邊界的字符。它同樣是個 0 長度字符。
‘(?:)’ 無捕獲組
當你要將一部分規則作爲一個整體對它進行某些操作,比如指定其重複次數時,你需要將這部分規則用 ’(?:’ ‘)’ 把它包圍起來,而不能僅僅只用一對括號,那樣將得到絕對出人意料的結果。
‘(?# )’ 註釋
Python 允許你在正則表達式中寫入註釋,在 ’(?#’ ‘)’ 之間的內容將被忽略。
‘*’ 0 或多次匹配
表示匹配前面的規則 0 次或多次。
‘+’ 1 次或多次匹配
表示匹配前面的規則至少 1 次,可以多次匹配
‘?’ 0 或 1 次匹配
只匹配前面的規則 0 次或 1 次。
‘{m}’ 精確匹配 m 次
‘{m,n}’ 匹配最少 m 次,最多 n 次。 (n>m)
如果你只想指定一個最少次數或只指定一個最多次數,你可以把另外一個參數空起來。比如你想指定最少 3 次,可以寫成 {3,} (注意那個逗號),同樣如果只想指定最大爲 5 次,可以寫成 { , 5} ,也可以寫成 {0,5} 。
‘*?’ ‘+?’ ‘??’ 最小匹配
‘*’ ‘+’ ‘?’ 通常都是儘可能多的匹配字符。有時候我們希望它儘可能少的匹配。
‘(?<=…)’ 前向界定
括號中 ’…’ 代表你希望匹配的字符串的前面應該出現的字符串。
‘(?=…)’ 後向界定
括號中的 ’…’ 代表你希望匹配的字符串後面應該出現的字符串。
‘(?<!...)’ 前向非界定
只有當你希望的字符串前面不是’…’ 的內容時才匹配
‘(?!...)’ 後向非界定
只有當你希望的字符串後面不跟着 ’…’ 內容時才匹配。
‘(‘’)’ 無命名組
最基本的組是由一對圓括號括起來的正則式。
‘(?P<name>…)’ 命名組
‘(?P’ 代表這是一個 Python 的語法擴展 ’<…>’ 裏面是你給這個組起的名字,比如你可以給一個全部由數字組成的組叫做 ’num’ ,它的形式就是 ’(?P<num>/d+)’ 。
‘(?P=name)’ 調用已匹配的命名組
要注意,再次調用的這個組是已被匹配的組,也就是說它裏面的內容是和前面命名組裏的內容是一樣的。
‘/number’ 通過序號調用已匹配的組
正則式中的每個組都有一個序號,序號是按組從左到右,從 1 開始的數字
‘(?( id/name )yes-pattern|no-pattern)’ 判斷指定組是否已匹配,執行相應的規則
這個規則的含義是,如果 id/name 指定的組在前面匹配成功了,則執行 yes-pattern 的正則式,否則執行 no-pattern 的正則式。
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 ) # 匹配位於字符串尾的數字
a = 'abc abcde bc bcd'
re.findall( r’/bbc/b’ , a) # 匹配一個單獨的單詞 ‘bc’ ,而當它是其它單詞的一部分的時候不匹配
re.findall( r’/sbc/s’ , a ) #匹配一個單獨的單詞 ‘bc’
re.findall( r’/Bbc/w+’ , a ) #匹配包含 ’bc’ 但不以 ’bc’ 爲開頭的單詞
b=’ababab abbabb aabaab’
re.findall( r’/b(?:ab)+/b’ , b)
re.findall( r’/b(ab)+/b’ , b)
c = ‘ aaa bbb111 cc22cc 33dd ‘
re.findall( r’/b[a-z]+/d*/b’ , c ) #必須至少 1 個字母開頭,以連續數字結尾或沒有數字
re.findall( r’[a-z]+/d*’ , c )
d = ‘ 123 10e3 20e4e4 30ee5 ‘
re.findall( r’ /b/d+[eE]?/d*/b’ , d )
num= ‘ 1 22 333 4444 55555 666666 ‘
re.findall( r’/b/d{3}/b’ , num ) # a : 3 位數
re.findall( r’/b/d{2,4}/b’ , num ) # b: 2 位數到 4 位數
re.findall( r’/b/d{5,}/b’, num ) # c: 5 位數以上的數
re.findall( r’/b/d{1,4}/b’ , num ) # 4 位數以下的數
e =r ‘/* part 1 */ code /* part 2 */’
re.findall( r’//*.*/*/’ , e )
re.findall( r’//*.*?/*/’ , e ) # 在 * 後面加上 ? ,表示儘可能少的匹配
f=r’/* comment 1 */ code /* comment 2 */’
re.findall( r’(?<=//*).+?(?=/*/)’ , f )
g = ‘aaa111aaa , bbb222 , 333ccc ‘
re.findall( r’(?<=[a-z]+)/d+(?=[a-z]+)' , g ) # 錯誤的用法
re.findall( r’/d+(?=[a-z]+)’, g )
re.findall (r'[a-z]+(/d+)[a-z]+' , g )
re.findall( r’/d+(?!/w+)’ , g )
re.findall( r’/d+(?![a-z]+)’ , g )
h = ‘aaa111aaa , bbb222 , 333ccc ‘
re.findall (r'[a-z]+(/d+)[a-z]+' , h )
k='aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'
re.findall( r'([a-z]+)/d+([a-z]+)' , k ) # 找出中間夾有數字的字母
re.findall( r '(?P<g1>[a-z]+)/d+(?P=g1)' , k ) # 找出被中間夾有數字的前後同樣的字母
re.findall( r'[a-z]+(/d+)([a-z]+)' , k ) # 找出前面有字母引導,中間是數字,後面是字母的字符串中的中間的數字和後面的字母
re.findall( r’([a-z]+)/d+/1’ , k )
m='111aaa222aaa111 , 333bbb444bb33'
re.findall( r'(/d+)([a-z]+)(/d+)(/2)(/1)' , m )
n='<usr1@mail1> usr2@maill2'
re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , n )
u='<usr1@mail1> usr2@maill2 <usr3@mail3 usr4@mail4> < usr5@mail5 '
re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , u )
運行結果:
['12', '56', '90']
['12']
['34', '78', '90']
['90']
['bc'] #只找到了那個單獨的 ’bc’
[' bc '] #只找到那個單獨的 ’bc’ ,不過注意前後有兩個空格,可能有點看不清楚
['bcde'] #成功匹配了 ’abcde’ 中的 ’bcde’ ,而沒有匹配 ’bcd’
['ababab']
['ab']
['aaa', 'bbb111']
['aaa', 'bbb111', 'cc22', 'cc', 'dd'] # 把單詞給拆開了
['123', '10e3']
['333']
['22', '333', '4444']
['55555', '666666']
['1', '22', '333', '4444']
[‘/* part 1 */ code /* part 2 */’]
['/* part 1 */', '/* part 2 */']
[' comment 1 ', ' comment 2 ']
['111', '333']
['111']
['222']
['11', '222', '33']
['111']
[('aaa', 'aaa'), ('fff', 'ggg')]
['aaa']
[('111', 'aaa'), ('777', 'ggg')]
['aaa']
[('111', 'aaa', '222', 'aaa', '111')]
[('<', 'usr1@mail1'), ('', 'usr2@maill2')]
[('<', 'usr1@mail1'), ('', 'usr2@maill2'), ('', 'usr3@mail3'), ('', 'usr4@mail4'), ('', 'usr5@mail5')]
match 與 search
match( rule , targetString [,flag] )
search( rule , targetString [,flag] )
match 從字符串的開頭開始匹配,如果開頭位置沒有匹配成功,就算失敗了;而 search 會跳過開頭,繼續向後尋找是否有匹配的字符串。針對不同的需要,可以靈活使用這兩個函數。
s= 'Tom:9527 , Sharry:0003'
m=re.match( r'(?P<name>/w+):(?P<num>/d+)' , s )
m.group()
m.groups()
m.group(‘name’)
m.group(‘num’)
運行結果:
'Tom:9527'
('Tom', '9527')
'Tom'
'9527'
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 對象。這在對每次返回的對象進行比較複雜的操作時比較有用。
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 '