正則表達式(regular expression)是可以匹配文本片段的模式颓芭。最簡單的正則表達式就是普通字符串顷锰,可以匹配其自身咨堤。比如鸽斟,正則表達式 ‘hello’ 可以匹配字符串 ‘hello’聚请。
要注意的是质帅,正則表達式并不是一個程序咐容,而是用于處理字符串的一種模式吏奸,如果你想用它來處理字符串薛耻,就必須使用支持正則表達式的工具帜矾,比如 Linux 中的 awk, sed, grep床玻,或者編程語言 Perl, Python, Java 等等毁涉。
正則表達式有多種不同的風格,下表列出了適用于 Python 或 Perl 等編程語言的部分元字符以及說明:
re 模塊
re 模塊提供了不少有用的函數(shù)锈死,用以匹配字符串薪丁,比如:
compile 函數(shù)
match 函數(shù)
search 函數(shù)
findall 函數(shù)
finditer 函數(shù)
split 函數(shù)
sub 函數(shù)
subn 函數(shù)
compile 函數(shù)
compile 函數(shù)用于編譯正則表達式,生成一個 Pattern 對象馅精,它的一般使用形式如下:
re.compile(pattern[, flag])
# 例子
import re
# 將正則表達式編譯成 Pattern 對象
pattern = re.compile(r'\d+')
在上面严嗜,我們已將一個正則表達式編譯成 Pattern 對象,接下來洲敢,我們就可以利用 pattern 的一系列方法對文本進行匹配查找了漫玄。Pattern 對象的一些常用方法主要有:
match 方法
search 方法
findall 方法
finditer 方法
split 方法
sub 方法
subn 方法
match 方法
match 方法用于查找字符串的頭部(也可以指定起始位置),它是一次匹配压彭,只要找到了一個匹配的結果就返回睦优,而不是查找所有匹配的結果。它的一般使用形式如下:
match(string[, pos[, endpos]])
其中壮不,string 是待匹配的字符串汗盘,pos 和 endpos 是可選參數(shù),指定字符串的起始和終點位置询一,默認值分別是 0 和 len (字符串長度)隐孽。因此,當你不指定 pos 和 endpos 時健蕊,match 方法默認匹配字符串的頭部菱阵。
當匹配成功時,返回一個 Match 對象缩功,如果沒有匹配上晴及,則返回 None。
看看例子嫡锌。
>>> import re
>>> pattern = re.compile(r'\d+') # 用于匹配至少一個數(shù)字
>>> m = pattern.match('one12twothree34four') # 查找頭部虑稼,沒有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 2, 10) # 從'e'的位置開始匹配琳钉,沒有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 3, 10) # 從'1'的位置開始匹配,正好匹配
>>> print m # 返回一個 Match 對象
<_sre.SRE_Match object at 0x10a42aac0>
>>> m.group(0) # 可省略 0
'12'
>>> m.start(0) # 可省略 0
3
>>> m.end(0) # 可省略 0
5
>>> m.span(0) # 可省略 0
(3, 5)
在上面蛛倦,當匹配成功時返回一個 Match 對象槽卫,其中:
group([group1, …]) 方法用于獲得一個或多個分組匹配的字符串,當要獲得整個匹配的子串時胰蝠,可直接使用 group() 或 group(0);
start([group]) 方法用于獲取分組匹配的子串在整個字符串中的起始位置(子串第一個字符的索引)震蒋,參數(shù)默認值為 0茸塞;
end([group]) 方法用于獲取分組匹配的子串在整個字符串中的結束位置(子串最后一個字符的索引+1),參數(shù)默認值為 0查剖;
span([group]) 方法返回 (start(group), end(group))钾虐。
再看看一個例子:
>>> import re
>>> pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小寫
>>> m = pattern.match('Hello World Wide Web')
>>> print m # 匹配成功,返回一個 Match 對象
<_sre.SRE_Match object at 0x10bea83e8>
>>> m.group(0) # 返回匹配成功的整個子串
'Hello World'
>>> m.span(0) # 返回匹配成功的整個子串的索引
(0, 11)
>>> m.group(1) # 返回第一個分組匹配成功的子串
'Hello'
>>> m.span(1) # 返回第一個分組匹配成功的子串的索引
(0, 5)
>>> m.group(2) # 返回第二個分組匹配成功的子串
'World'
>>> m.span(2) # 返回第二個分組匹配成功的子串
(6, 11)
>>> m.groups() # 等價于 (m.group(1), m.group(2), ...)
('Hello', 'World')
>>> m.group(3) # 不存在第三個分組
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: no such group
search 方法
search 方法用于查找字符串的任何位置笋庄,它也是一次匹配效扫,只要找到了一個匹配的結果就返回,而不是查找所有匹配的結果直砂,它的一般使用形式如下:
search(string[, pos[, endpos]])
其中菌仁,string 是待匹配的字符串,pos 和 endpos 是可選參數(shù)静暂,指定字符串的起始和終點位置济丘,默認值分別是 0 和 len (字符串長度)。
當匹配成功時洽蛀,返回一個 Match 對象摹迷,如果沒有匹配上,則返回 None郊供。
讓我們看看例子:
>>> import re
>>> pattern = re.compile('\d+')
>>> m = pattern.search('one12twothree34four') # 這里如果使用 match 方法則不匹配
>>> m
<_sre.SRE_Match object at 0x10cc03ac0>
>>> m.group()
'12'
>>> m = pattern.search('one12twothree34four', 10, 30) # 指定字符串區(qū)間
>>> m
<_sre.SRE_Match object at 0x10cc03b28>
>>> m.group()
'34'
>>> m.span()
(13, 15)
再來看一個例子:
# -*- coding: utf-8 -*-
import re
# 將正則表達式編譯成 Pattern 對象
pattern = re.compile(r'\d+')
# 使用 search() 查找匹配的子串峡碉,不存在匹配的子串時將返回 None
# 這里使用 match() 無法成功匹配
m = pattern.search('hello 123456 789')
if m:
# 使用 Match 獲得分組信息
print 'matching string:',m.group()
print 'position:',m.span()
# 執(zhí)行結果:
# matching string: 123456
# position: (6, 12)
findall 方法
上面的 match 和 search 方法都是一次匹配,只要找到了一個匹配的結果就返回驮审。然而鲫寄,在大多數(shù)時候,我們需要搜索整個字符串疯淫,獲得所有匹配的結果塔拳。
findall 方法的使用形式如下:
findall(string[, pos[, endpos]])
其中,string 是待匹配的字符串峡竣,pos 和 endpos 是可選參數(shù)靠抑,指定字符串的起始和終點位置,默認值分別是 0 和 len (字符串長度)适掰。
findall 以列表形式返回全部能匹配的子串颂碧,如果沒有匹配荠列,則返回一個空列表。
看看例子:
import re
pattern = re.compile(r'\d+') # 查找數(shù)字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 10)
print result1
print result2
# 執(zhí)行結果:
# ['123456', '789']
# ['1', '2']
finditer 方法
finditer 方法的行為跟 findall 的行為類似载城,也是搜索整個字符串肌似,獲得所有匹配的結果。但它返回一個順序訪問每一個匹配結果(Match 對象)的迭代器诉瓦。
看看例子:
# -*- coding: utf-8 -*-
import re
pattern = re.compile(r'\d+')
result_iter1 = pattern.finditer('hello 123456 789')
result_iter2 = pattern.finditer('one1two2three3four4', 0, 10)
print type(result_iter1)
print type(result_iter2)
print 'result1...'
for m1 in result_iter1: # m1 是 Match 對象
print 'matching string: {}, position: {}'.format(m1.group(), m1.span())
print 'result2...'
for m2 in result_iter2:
print 'matching string: {}, position: {}'.format(m2.group(), m2.span())
# 執(zhí)行結果:
"""
<type 'callable-iterator'>
<type 'callable-iterator'>
result1...
matching string: 123456, position: (6, 12)
matching string: 789, position: (13, 16)
result2...
matching string: 1, position: (3, 4)
matching string: 2, position: (7, 8)
"""
split 方法
split 方法按照能夠匹配的子串將字符串分割后返回列表川队,它的使用形式如下:
split(string[, maxsplit])
其中,maxsplit 用于指定最大分割次數(shù)睬澡,不指定將全部分割固额。
看看例子:
import re
p = re.compile(r'[\s\,\;]+')
print p.split('a,b;; c d')
# 執(zhí)行結果:
# ['a', 'b', 'c', 'd']
sub 方法
sub 方法用于替換。它的使用形式如下:
sub(repl, string[, count])
其中煞聪,repl 可以是字符串也可以是一個函數(shù):
如果 repl 是字符串斗躏,則會使用 repl 去替換字符串每一個匹配的子串,并返回替換后的字符串昔脯,另外啄糙,repl 還可以使用 \id 的形式來引用分組,但不能使用編號 0云稚;
如果 repl 是函數(shù)隧饼,這個方法應當只接受一個參數(shù)(Match 對象),并返回一個字符串用于替換(返回的字符串中不能再引用分組)静陈。
count 用于指定最多替換次數(shù)桑李,不指定時全部替換。
看看例子:
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123, hello 456'
def func(m):
return 'hi' + ' ' + m.group(2)
print p.sub(r'hello world', s) # 使用 'hello world' 替換 'hello 123' 和 'hello 456'
print p.sub(r'\2 \1', s) # 引用分組
print p.sub(func, s)
print p.sub(func, s, 1) # 最多替換一次
# 執(zhí)行結果:
"""
hello world, hello world
123 hello, 456 hello
hi 123, hi 456
hi 123, hello 456
"""
subn 方法
subn 方法跟 sub 方法的行為類似窿给,也用于替換贵白。它的使用形式如下:
subn(repl, string[, count])
它返回一個元組:
(sub(repl, string[, count]), 替換次數(shù))
元組有兩個元素,第一個元素是使用 sub 方法的結果崩泡,第二個元素返回原字符串被替換的次數(shù)禁荒。
看看例子:
import re
p = re.compile(r'(\w+) (\w+)')
s = 'hello 123, hello 456'
def func(m):
return 'hi' + ' ' + m.group(2)
print p.subn(r'hello world', s)
print p.subn(r'\2 \1', s)
print p.subn(func, s)
print p.subn(func, s, 1)
執(zhí)行結果:
"""
('hello world, hello world', 2)
('123 hello, 456 hello', 2)
('hi 123, hi 456', 2)
('hi 123, hello 456', 1)
"""
其他函數(shù)
事實上,使用 compile 函數(shù)生成的 Pattern 對象的一系列方法跟 re 模塊的多數(shù)函數(shù)是對應的角撞,但在使用上有細微差別呛伴。
match 函數(shù)
match 函數(shù)的使用形式如下:
re.match(pattern, string[, flags]):
其中,pattern 是正則表達式的字符串形式谒所,比如 \d+, [a-z]+热康。
而 Pattern 對象的 match 方法使用形式是:
match(string[, pos[, endpos]])
可以看到,match 函數(shù)不能指定字符串的區(qū)間劣领,它只能搜索頭部姐军,看看例子:
import re
m1 = re.match(r'\d+', 'One12twothree34four')
if m1:
print 'matching string:',m1.group()
else:
print 'm1 is:',m1
m2 = re.match(r'\d+', '12twothree34four')
if m2:
print 'matching string:', m2.group()
else:
print 'm2 is:',m2
# 執(zhí)行結果:
# m1 is: None
# matching string: 12
search 函數(shù)
search 函數(shù)的使用形式如下:
re.search(pattern, string[, flags])
search 函數(shù)不能指定字符串的搜索區(qū)間,用法跟 Pattern 對象的 search 方法類似。
findall 函數(shù)
findall 函數(shù)的使用形式如下:
re.findall(pattern, string[, flags])
findall 函數(shù)不能指定字符串的搜索區(qū)間奕锌,用法跟 Pattern 對象的 findall 方法類似著觉。
看看例子:
import re
print re.findall(r'\d+', 'hello 12345 789')
# 輸出
# ['12345', '789']
finditer 函數(shù)
finditer 函數(shù)的使用方法跟 Pattern 的 finditer 方法類似,形式如下:
re.finditer(pattern, string[, flags])
split 函數(shù)
split 函數(shù)的使用形式如下:
re.split(pattern, string[, maxsplit])
sub 函數(shù)
sub 函數(shù)的使用形式如下:
re.sub(pattern, repl, string[, count])
subn 函數(shù)
subn 函數(shù)的使用形式如下:
re.subn(pattern, repl, string[, count])
到底用哪種方式
從上文可以看到惊暴,使用 re 模塊有兩種方式:
使用 re.compile 函數(shù)生成一個 Pattern 對象饼丘,然后使用 Pattern 對象的一系列方法對文本進行匹配查找;
直接使用 re.match, re.search 和 re.findall 等函數(shù)直接對文本匹配查找辽话;
下面肄鸽,我們用一個例子展示這兩種方法。
先看第 1 種用法:
import re
# 將正則表達式先編譯成 Pattern 對象
pattern = re.compile(r'\d+')
print pattern.match('123, 123')
print pattern.search('234, 234')
print pattern.findall('345, 345')
再看第 2 種用法:
import re
print re.match(r'\d+', '123, 123')
print re.search(r'\d+', '234, 234')
print re.findall(r'\d+', '345, 345')
如果一個正則表達式需要用到多次(比如上面的 \d+)油啤,在多種場合經(jīng)常需要被用到典徘,出于效率的考慮,我們應該預先編譯該正則表達式村砂,生成一個 Pattern 對象,再使用該對象的一系列方法對需要匹配的文件進行匹配屹逛;而如果直接使用 re.match, re.search 等函數(shù)础废,每次傳入一個正則表達式,它都會被編譯一次罕模,效率就會大打折扣评腺。
因此,我們推薦使用第 1 種用法淑掌。
匹配中文
在某些情況下蒿讥,我們想匹配文本中的漢字,有一點需要注意的是抛腕,中文的 unicode 編碼范圍 主要在 [\u4e00-\u9fa5]芋绸,這里說主要是因為這個范圍并不完整,比如沒有包括全角(中文)標點担敌,不過摔敛,在大部分情況下,應該是夠用的全封。
假設現(xiàn)在想把字符串 title = u'你好马昙,hello,世界' 中的中文提取出來刹悴,可以這么做:
# -*- coding: utf-8 -*-
import re
title = u'你好行楞,hello,世界'
pattern = re.compile(ur'[\u4e00-\u9fa5]+')
result = pattern.findall(title)
print result
注意到土匀,我們在正則表達式前面加上了兩個前綴 ur子房,其中 r 表示使用原始字符串,u 表示是 unicode 字符串。
執(zhí)行結果:
[u'\u4f60\u597d', u'\u4e16\u754c']
貪婪匹配
在 Python 中池颈,正則匹配默認是貪婪匹配(在少數(shù)語言中可能是非貪婪)尾序,也就是匹配盡可能多的字符。
比如躯砰,我們想找出字符串中的所有 div 塊:
import re
content = 'aa<div>test1</div>bb<div>test2</div>cc'
pattern = re.compile(r'<div>.*</div>')
result = pattern.findall(content)
print result
# 執(zhí)行結果:
# ['<div>test1</div>bb<div>test2</div>']
由于正則匹配是貪婪匹配每币,也就是盡可能多的匹配,因此琢歇,在成功匹配到第一個 </div> 時兰怠,它還會向右嘗試匹配,查看是否還有更長的可以成功匹配的子串李茫。
如果我們想非貪婪匹配揭保,可以加一個 ?,如下:
import re
content = 'aa<div>test1</div>bb<div>test2</div>cc'
pattern = re.compile(r'<div>.*?</div>') # 加上 ?
result = pattern.findall(content)
print result
# 結果:
# ['<div>test1</div>', '<div>test2</div>']
小結
re 模塊的一般使用步驟如下:
使用 compile 函數(shù)將正則表達式的字符串形式編譯為一個 Pattern 對象魄宏;
通過 Pattern 對象提供的一系列方法對文本進行匹配查找秸侣,獲得匹配結果(一個 Match 對象);
最后使用 Match 對象提供的屬性和方法獲得信息宠互,根據(jù)需要進行其他的操作味榛;
Python 的正則匹配默認是貪婪匹配。