re模塊
在Python中遵馆,通過內(nèi)置的re模塊提供對(duì)正則表達(dá)式的支持。正則表達(dá)式會(huì)被編譯成一系列的字節(jié)碼,然后由通過C編寫的正則表達(dá)式引擎進(jìn)行執(zhí)行古今。該引擎自從Python1.6被內(nèi)置以來,近20年時(shí)間未有發(fā)生過變化
re模塊支持下面的正則語(yǔ)法:
"."
"^"
"$"
"*"
"+"
"?"
*?,+?,??
{m,n}
{m,n}?
"\\"
[]
"|"
(...)
(?aiLmsux)
(?:...)
(?P<name>...)
(?P=name)
(?#...)
(?=...)
(?!...)
(?<=...)
(?<!...)
(?(id/name)yes|no)
提供了下面的方法進(jìn)行字符串的查找滔以、替換和分割等各種處理操作
方法 | 描述 | 返回值 |
---|---|---|
compile(pattern[, flags]) | 根據(jù)包含正則表達(dá)式的字符串創(chuàng)建模式對(duì)象 | re對(duì)象 |
search(pattern, string[, flags]) | 在字符串中查找 | 第一個(gè)匹配到的對(duì)象或者None |
match(pattern, string[, flags]) | 在字符串的開始處匹配模式 | 在字符串開頭匹配到的對(duì)象或者None |
split(pattern, string[, maxsplit=0,flags]) | 根據(jù)模式的匹配項(xiàng)來分割字符串 | 分割后的字符串列表 |
findall(pattern, string,flags) | 列出字符串中模式的所有匹配項(xiàng) | 所有匹配到的字符串列表 |
sub(pat,repl, string[,count=0,flags]) | 將字符串中所有的pat的匹配項(xiàng)用repl替換 | 完成替換后的新字符串 |
finditer(pattern, string,flags) | 將所有匹配到的項(xiàng)生成一個(gè)迭代器 | 所有匹配到的字符串組合成的迭代器 |
subn(pat,repl, string[,count=0,flags]) | 在替換字符串后捉腥,同時(shí)報(bào)告替換的次數(shù) | 完成替換后的新字符串及替換次數(shù) |
escape(string) | 將字符串中所有特殊正則表達(dá)式字符串轉(zhuǎn)義 | 轉(zhuǎn)義后的字符串 |
purge(pattern) | 清空正則表達(dá)式 | |
template(pattern[,flags]) | 編譯一個(gè)匹配模板 | 模式對(duì)象 |
fullmatch(pattern, string[, flags]) | match方法的全字符串匹配版本 | 類似match的返回值 |
反斜杠的困擾:\
與大多數(shù)編程語(yǔ)言相同,正則表達(dá)式里使用\
作為轉(zhuǎn)義字符你画,這可能造成反斜杠困擾抵碟。假如需要匹配文本中的字符\
,那么使用編程語(yǔ)言表示的正則表達(dá)式里將需要4個(gè)反斜杠\\\\
坏匪。前兩個(gè)和后兩個(gè)分別用于在編程語(yǔ)言里轉(zhuǎn)義成反斜杠拟逮,轉(zhuǎn)換成兩個(gè)反斜杠后再在正則表達(dá)式里轉(zhuǎn)義成一個(gè)反斜杠。為了方便我們使用個(gè)适滓,Python提供了原生字符串的功能敦迄,很好地解決了這個(gè)問題,這個(gè)例子中的正則表達(dá)式可以使用r"\\"
表示凭迹。同樣罚屋,匹配一個(gè)數(shù)字的"\\d"
可以直接寫成r"\d"
。有了原生字符串嗅绸,你再也不用擔(dān)心是不是漏寫了反斜杠脾猛,寫出來的表達(dá)式也更直觀。
compile(pattern, flags=0)
這個(gè)方法是re模塊的工廠方法鱼鸠,用于將字符串形式的正則表達(dá)式編譯為Pattern模式對(duì)象猛拴,可以實(shí)現(xiàn)更高效率的匹配喉刘。第二個(gè)參數(shù)flag是匹配模式。
使用compile()
完成一次轉(zhuǎn)換后漆弄,再次使用該匹配模式的時(shí)候就不用進(jìn)行轉(zhuǎn)換了睦裳。經(jīng)過compile()
轉(zhuǎn)換的正則表達(dá)式對(duì)象也能使用普通的re方法。其用法如下:
import re
pat = re.compile(r"abc")
pat.match("abc123")
<_sre.SRE_Match object; span=(0, 3), match='abc'>
經(jīng)過compile()方法編譯過后的返回值是個(gè)re對(duì)象撼唾,它可以調(diào)用match()廉邑、search()、findall()等其他方法,但其他方法不能調(diào)用compile()方法倒谷。實(shí)際上蛛蒙,match()和search()等方法在使用前,Python內(nèi)部幫你進(jìn)行了compile的步驟渤愁。
re.match(r"abc","abc123").compile()
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
re.match(r"abc","abc123").compile()
AttributeError: '_sre.SRE_Match' object has no attribute 'compile'
match(pattern, string, flags=0)
match()方法會(huì)在給定字符串的開頭進(jìn)行匹配牵祟,如果匹配不成功則返回None,匹配成功返回一個(gè)匹配對(duì)象抖格,這個(gè)對(duì)象有個(gè)group()方法诺苹,可以將匹配到的字符串給出。
ret = re.match(r"abc","ab1c123")
print(ret)
None
re.match(r"abc","abc123")
<_sre.SRE_Match object; span=(0, 3), match='abc'>
obj = re.match(r"abc","abc123")
obj.group()
'abc'
search(pattern, string, flags=0)
在文本內(nèi)查找雹拄,返回第一個(gè)匹配到的字符串收奔。它的返回值類型和使用方法與match()是一樣的,唯一的區(qū)別就是查找的位置不用固定在文本的開頭滓玖。
obj = re.search(r"abc","123abc456abc789")
obj
<_sre.SRE_Match object; span=(3, 6), match='abc'>
obj.group()
'abc'
obj.start()
3
obj.end()
6
obj.span()
(3, 6)
findall(pattern, string, flags=0)
作為re模塊的三大搜索函數(shù)之一坪哄,findall()和match()、search()的不同之處在于势篡,前兩者都是單值匹配翩肌,找到一個(gè)就忽略后面,直接返回不再查找了禁悠。而findall是全文查找念祭,它的返回值是一個(gè)匹配到的字符串的列表。這個(gè)列表沒有g(shù)roup()方法绷蹲,沒有start棒卷、end、span祝钢,更不是一個(gè)匹配對(duì)象比规,僅僅是個(gè)列表!如果一項(xiàng)都沒有匹配到那么返回一個(gè)空列表拦英。
obj = re.findall(r"abc","123abc456abc789")
obj
['abc', 'abc']
obj.group()
Traceback (most recent call last):
File "<pyshell#37>", line 1, in <module>
obj.group()
AttributeError: 'list' object has no attribute 'group'
obj = re.findall(r"ABC","123abc456abc789")
print(obj)
[]
split(pattern, string, maxsplit=0, flags=0)
re模塊的split()方法和字符串的split()方法很相似蜒什,都是利用特定的字符去分割字符串。但是re模塊的split()可以使用正則表達(dá)式疤估,因此更靈活灾常,更強(qiáng)大霎冯,而且還有“殺手锏” 。
s = "8+7*5+6/3"
import re
a_list = re.split(r"[\+\-\*\/]",s)
a_list
['8', '7', '5', '6', '3']
split有個(gè)參數(shù)maxsplit
钞瀑,用于指定分割的次數(shù):
a_list = re.split(r"[\+\-\*\/]",s,maxsplit= 2)
a_list
['8', '7', '5+6/3']
sub(pattern, repl, string, count=0, flags=0)
sub()方法類似字符串的replace()方法沈撞,用指定的內(nèi)容替換匹配到的字符,可以指定替換次數(shù)雕什。
s = "i am jack! i am nine years old ! i like swiming!"
import re
s = re.sub(r"i","I",s)
s
'I am jack! I am nIne years old ! I lIke swImIng!'
分組功能
Python的re模塊有一個(gè)分組功能缠俺。所謂的分組就是去已經(jīng)匹配到的內(nèi)容里面再篩選出需要的內(nèi)容,相當(dāng)于二次過濾贷岸。實(shí)現(xiàn)分組靠圓括號(hào)()
壹士,而獲取分組的內(nèi)容靠的是group()、groups()和groupdict()方法偿警,其實(shí)前面我們已經(jīng)展示過躏救。re模塊里的幾個(gè)重要方法在分組上,有不同的表現(xiàn)形式螟蒸,需要區(qū)別對(duì)待盒使。
例一:match()方法,不分組時(shí)的情況:
import re
origin = "hasdfi123123safd"
# 不分組時(shí)的情況
r = re.match("h\w+", origin)
print(r.group()) # 獲取匹配到的整體結(jié)果
print(r.groups()) # 獲取模型中匹配到的分組結(jié)果元組
print(r.groupdict()) # 獲取模型中匹配到的分組中所有key的字典
結(jié)果:
hasdfi123123safd
()
{}
例二:match()方法尿庐,有分組的情況(注意圓括號(hào)V也馈)
import re
origin = "hasdfi123123safd123"
# 有分組
r = re.match("h(\w+).*(?P<name>\d)$", origin)
print(r.group()) # 獲取匹配到的整體結(jié)果
print(r.group(1)) # 獲取匹配到的分組1的結(jié)果
print(r.group(2)) # 獲取匹配到的分組2的結(jié)果
print(r.groups()) # 獲取模型中匹配到的分組結(jié)果元組
print(r.groupdict()) # 獲取模型中匹配到的分組中所有key的字典
執(zhí)行結(jié)果:
hasdfi123123safd123
asdfi123123safd12
3
('asdfi123123safd12', '3')
{'name': '3'}
例三呢堰,search()方法抄瑟,有分組的情況:
import re
origin = "sdfi1ha23123safd123" # 注意這里對(duì)匹配對(duì)象做了下調(diào)整
# 有分組
r = re.search("h(\w+).*(?P<name>\d)$", origin)
print(r.group())
print(r.group(0))
print(r.group(1))
print(r.group(2))
print(r.groups())
print(r.groupdict())
執(zhí)行結(jié)果:
ha23123safd123
ha23123safd123
a23123safd12
3
('a23123safd12', '3')
{'name': '3'}
例四,findall()方法枉疼,沒有分組的情況:
import re
origin = "has something have do"
# 無(wú)分組
r = re.findall("h\w+", origin)
print(r)
執(zhí)行結(jié)果:
['has', 'hing', 'have']
# 一切看起來沒什么不一樣
例五皮假,findall()方法,有一個(gè)分組的情況:
import re
origin = "has something have do"
# 一個(gè)分組
r = re.findall("h(\w+)", origin)
print(r)
執(zhí)行結(jié)果:
['as', 'ing', 'ave']
例六骂维,findall()方法惹资,有兩個(gè)以上分組的情況:
import re
origin = "hasabcd something haveabcd do" # 字符串調(diào)整了一下
# 兩個(gè)分組
r = re.findall("h(\w+)a(bc)d", origin)
print(r)
運(yùn)行結(jié)果:
[('as', 'bc'), ('ave', 'bc')]
如何拆分含有多種分隔符的字符串
我們要把某字符串依據(jù)分隔符號(hào)拆分不同的字段,該字符串包含多種不同的分隔符航闺,例如:
s = 'ab;cd|efg|hi|hi,jkl\topq;str,ubw\asyd'
其中<,>,<;>,<|>,<\t> 都是分隔符號(hào),如何處理褪测?
第一種方法,可以連續(xù)使用str.split()方法潦刃,每次處理一種分割符號(hào)
def mySplit(s,ds):
res = [s]
for d in ds:
t = []
list(map(lambda x:t.extend(x.split(d)),res))
res = t
return res
s = 'ab;cd|efg|hi|hi,jkl\topq;str,ubw\x07syd'
print(mySplit(s,',;|\t'))
如果出現(xiàn)連續(xù)的分割符號(hào)侮措,會(huì)出現(xiàn)空字符的情況,這時(shí)候如果我們想去掉空字符串乖杠。
return [x for x in res if x]
用列表推導(dǎo)式可以完成這個(gè)需求分扎。
第二種方法,使用正則表達(dá)式的re.split()方法胧洒,一次性拆分字符串
import re
re.split(r'[,;\t|]+',s)
某軟件要求畏吓,從網(wǎng)絡(luò)抓取各個(gè)城市氣溫信息墨状,并依次顯示
北京:15-17
天津:17-22
長(zhǎng)春:12-18
如果一次抓取所有城市天氣再顯示,顯示第一個(gè)城市氣溫時(shí)菲饼,有很高的延遲肾砂,并且浪費(fèi)存儲(chǔ)空間,我們期望以用時(shí)訪問的策略宏悦,并且能把所有城市氣溫封裝到一個(gè)對(duì)象里通今,可用for語(yǔ)句進(jìn)行迭代,如何解決肛根?
import requests
def getWeather(city):
r = requests.get('http://wthrcdn.etouch.cn/weather_mini?city='+city)
data = r.json()['data']['forecast'][0]
return '%s:%s,%s'%(city,data['low'],data['high'])
print(getWeather('北京'))
如何調(diào)整字符串中文本的格式
日期格式為'yyyy-mm-dd':
我們想把其中日期改為美國(guó)日期的格式'mm/dd/yyyy'
'2016-05-23'=>'05/23/2016',應(yīng)該如何處理辫塌?
import re
re.sub('(\d{4})-(\d{2})-(\d{2})',r'\2/\3/\1',file)
re.sub('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',r'\g<month>/\g<day>/\g<year>',file)