正則表達(dá)式
1. 正則表達(dá)式概述
- 正則表達(dá)式缺菌,又稱(chēng)正規(guī)表示式薇溃、正規(guī)表示法、正規(guī)表達(dá)式梅肤、規(guī)則表達(dá)式司蔬、常規(guī)表示法(英語(yǔ):Regular Expression,在代碼中常簡(jiǎn)寫(xiě)為regex姨蝴、regexp或RE)俊啼,是計(jì)算機(jī)科學(xué)的一個(gè)概念。正則表達(dá)式使用單個(gè)字符串來(lái)描述似扔、匹配一系列匹配某個(gè)句法規(guī)則的字符串吨些。在很多文本編輯器里搓谆,正則表達(dá)式通常被用來(lái)檢索炒辉、替換那些匹配某個(gè)模式的文本。
- Regular Expression的“Regular”一般被譯為“正則”泉手、“正規(guī)”黔寇、“常規(guī)”。此處的“Regular”即是“規(guī)則”斩萌、“規(guī)律”的意思缝裤,Regular Expression即“描述某種規(guī)則的表達(dá)式”之意。
2. 模塊操作
在Python中需要通過(guò)正則表達(dá)式對(duì)字符串進(jìn)行匹配的時(shí)候颊郎,可以使用一個(gè)模塊憋飞,名字為re
2.1 re模塊的使用過(guò)程
# 導(dǎo)入re模塊
import re
# 使用match方法進(jìn)行匹配操作
result = re.match(正則表達(dá)式,要匹配的字符串)
# 如果上一步匹配到數(shù)據(jù)的話,可以使用group方法來(lái)提取數(shù)據(jù)
result.group()
- **re.match **
查詢標(biāo)志
大部分查詢方法還可以接受一個(gè)查詢標(biāo)志參數(shù)姆吭。查詢標(biāo)志讓正則表達(dá)式具有不同的行為榛做。下面一一說(shuō)明。
標(biāo)志 | 作用 |
---|---|
re.A内狸、 re.ASCII |
以ASCII模式查詢检眯,默認(rèn)是Unicode模式 |
re.DEBUG | 顯示編譯表達(dá)式的調(diào)試信息 |
re.I、re.IGNORECASE | 忽略字母的大小寫(xiě) |
re.L昆淡、re.LOCALE | 以區(qū)域敏感方式查詢匹配 |
re.M锰瘸、re.MULTILINE | 開(kāi)啟多行模式,開(kāi)啟之后行邊界符^$會(huì)匹配每行的開(kāi)始和結(jié)束昂灵,而不是整個(gè)字符串的開(kāi)始和結(jié)束 |
re.S避凝、re.DOTALL | 使用此標(biāo)志舞萄,會(huì)讓點(diǎn)符號(hào)匹配所有字符,默認(rèn)情況下點(diǎn)符號(hào)會(huì)匹配換行符以外的符號(hào) |
re.X管削、re.VERBOSE | 開(kāi)啟啰嗦模式鹏氧,可以在寫(xiě)正則表達(dá)式的時(shí)候添加注釋 |
re.match
是用來(lái)進(jìn)行正則匹配檢查的方法,若字符串匹配正則表達(dá)式佩谣,則match方法返回匹配對(duì)象(Match Object)把还,否則返回None(注意不是空字符串"")。
匹配對(duì)象Macth Object具有g(shù)roup方法茸俭,用來(lái)返回字符串的匹配部分吊履。
2.2re模塊示例(匹配以taobao開(kāi)頭的語(yǔ)句)
>>>import re
>>>re.match('taobao', 'taobao.com')
<_sre.SRE_Match object; span=(0, 6), match='taobao'>
>>>re.match('taobao', 'taobao.com').group()
'taobao'
說(shuō)明
- re.match() 能夠匹配出以xxx開(kāi)頭的字符串,是從第一個(gè)字符開(kāi)始匹配的
3. 表示字符
字符 | 功能 |
---|---|
中文 | [\u4e00-\u9fa5] |
. | 匹配任意一個(gè)字符(除了\n) |
[ ] | 匹配[ ]中列舉的一個(gè)字符 |
\d | 匹配數(shù)字,即0-9 |
\D | 匹配非數(shù)字调鬓,即不是數(shù)字 |
\s | 匹配空白艇炎,即空格,tab鍵 |
\S | 匹配非空白 |
\w | 匹配單詞字符腾窝,即a-z缀踪、A-Z、0-9虹脯、_驴娃,還有各個(gè)國(guó)家的文字 |
\W | 匹配非單詞字符 |
- .匹配任意一個(gè)字符(除了\n)
>>>import re
>>>re.match('.','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('.','abc').group()
'a'
>>>re.match('.','\nabc')
>>>re.match('.','\tabc')
<_sre.SRE_Match object; span=(0, 1), match='\t'>
>>>re.match('.','\tabc').group()
'\t'
匹配[ ]中列舉的一個(gè)字符
>>>import re
>>>re.match("[hH]","hello Python")
<_sre.SRE_Match object; span=(0, 1), match='h'>
>>>re.match("[hH]","Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='H'>
>>>re.match("[0-9]","7Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='7'>
\w 匹配單詞字符,即a-z循集、A-Z唇敞、0-9、_咒彤,還有各個(gè)國(guó)家的文字疆柔。加上第三個(gè)參數(shù)re.ASCII
就是以ASCII碼匹配,就只能匹配字母镶柱、數(shù)字旷档、下劃線
>>>import re
>>>re.match('\w','かきく')
<_sre.SRE_Match object; span=(0, 1), match='か'>
>>>re.match('\w','???')
<_sre.SRE_Match object; span=(0, 1), match='?'>
>>>re.match('\w','漢字')
<_sre.SRE_Match object; span=(0, 1), match='漢'>
>>>re.match('\w','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('\w','漢字',re.ASCII)#這個(gè)就沒(méi)有匹配成功
>>>re.match('\w','a漢字',re.ASCII)
<_sre.SRE_Match object; span=(0, 1), match='a'>
4. 原始字符串
在很多編程語(yǔ)言中,由于有轉(zhuǎn)義字符這么一種東西的存在歇拆,導(dǎo)致正則表達(dá)式需要使用兩個(gè)斜杠來(lái)處理鞋屈。
- Python中字符串前面加上 r 表示原生字符串,
- 與大多數(shù)編程語(yǔ)言相同查吊,正則表達(dá)式里使用""作為轉(zhuǎn)義字符谐区,這就可能造成反斜杠困擾。假如你需要匹配文本中的字符""逻卖,那么使用編程語(yǔ)言表示的正則表達(dá)式里將需要4個(gè)反斜杠 "\" :前 兩個(gè)和后兩個(gè)分別用于在編程語(yǔ)言里轉(zhuǎn)義成反斜杠宋列,轉(zhuǎn)換成兩個(gè)反斜杠后再在正則表達(dá)式里轉(zhuǎn)義成一個(gè)反斜杠。
- Python里的原生字符串很好地解決了這個(gè)問(wèn)題评也,有了原始字符串炼杖,你再也不用擔(dān)心是不是漏寫(xiě)了反斜杠灭返,寫(xiě)出來(lái)的表達(dá)式也更直觀。
>>>import re
>>>m = "c:\\a\\b\\c"
>>>m
'c:\\a\\b\\c'
>>>print(m)
c:\a\b\c
>>>re.match("c:\\\\",m).group()
'c:\\'
>>>print(re.match("c:\\\\",m).group())
c:\
>>>re.match(r"c:\\a",m).group()
'c:\\a'
>>>print(re.match(r"c:\\a",m).group())
c:\a
5. 表示數(shù)量
匹配多個(gè)字符的相關(guān)格式
字符 | 功能 |
---|---|
* | 匹配前面的子表達(dá)式任意次,即可有可無(wú) |
+ | 匹配前一個(gè)字符出現(xiàn)1次或者無(wú)限次坤邪,即最少有一次 |
? | 匹配前一個(gè)字符出現(xiàn)1次或者0次熙含,即要么有1次,要么沒(méi)有 |
{m} | 匹配前一個(gè)字符出現(xiàn)m次 |
{m,} | 匹配前一個(gè)字符至少出現(xiàn)m 次 |
{m,n} | 匹配前一個(gè)字符出現(xiàn)從m到n次 |
示例1 *
匹配出艇纺,一個(gè)字符串第一個(gè)字母為大小字符怎静,后面都是小寫(xiě)字母并且這些小寫(xiě)字母可有可無(wú)
>>>import re
>>>re.match("[A-Z][a-z]*","Aabcdef")
<_sre.SRE_Match object; span=(0, 7), match='Aabcdef'>
示例2:?
需求:匹配出,0到99之間的數(shù)字
>>>import re
re.match("[1-9]?[0-9]","33")
>>><_sre.SRE_Match object; span=(0, 2), match='33'>
>>>re.match("[1-9]?[0-9]","09")
<_sre.SRE_Match object; span=(0, 1), match='0'>
>>>re.match("[1-9]?[0-9]","9")
<_sre.SRE_Match object; span=(0, 1), match='9'>
示例3:{m,n}
需求:匹配出黔衡,8到20位的密碼蚓聘,可以是大小寫(xiě)英文字母、數(shù)字盟劫、下劃線
>>>import re
>>>re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
<_sre.SRE_Match object; span=(0, 18), match='1ad12f23s34455ff66'>
示例4:
匹配出163的郵箱地址夜牡,且@符號(hào)之前有4到20位,例如hello@163.com
import re
print('6~18個(gè)字符侣签,可使用字母塘装、數(shù)字、下劃線影所,需以字母開(kāi)頭')
while True:
address=input('>>>輸入郵箱地址:')
ret=re.match('[a-zA-Z]\w{3,19}@163\.com',address)
if ret == None:
print('輸入有誤蹦肴,請(qǐng)重新輸入。郵箱格式為6~18個(gè)字符型檀,可使用字母冗尤、數(shù)字、下劃線胀溺,需以字母開(kāi)頭')
continue
else:
ret = ret.group()
print('您的郵箱地址:%s'%ret)
break
運(yùn)行結(jié)果
G:\tools\python3.5\python.exe E:/workspace/day29/練習(xí)1.py
6~18個(gè)字符,可使用字母皆看、數(shù)字仓坞、下劃線,需以字母開(kāi)頭
>>>輸入郵箱地址:haha
輸入有誤腰吟,請(qǐng)重新輸入无埃。郵箱格式為6~18個(gè)字符,可使用字母毛雇、數(shù)字嫉称、下劃線,需以字母開(kāi)頭
>>>輸入郵箱地址:haha@163.com
您的郵箱地址:haha@163.com
6. 表示邊界
字符 | 功能 |
---|---|
^ | 匹配字符串的開(kāi)頭 |
$ | 匹配字符串的末尾 |
\b | 匹配一個(gè)單詞的邊界 |
\B | 匹配非單詞邊界 |
示例 $
>>>import re
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>
>>>re.match("[\w]{4,20}@163\.com$", "xiaoWang@163.comheihei")
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>
7. 匹配分組
字符 | 功能 |
---|---|
| | 匹配左右任意一個(gè)表達(dá)式 |
(ab) | 將括號(hào)中字符作為一個(gè)分組 |
\num | 引用分組num匹配到的字符串 |
(?P<name>) | 分組起別名 |
(?P=name) | 引用別名為name分組匹配到的字符串 |
示例 |
>>>import re
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@gmail.com")
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com")
<_sre.SRE_Match object; span=(0, 11), match='test@qq.com'>
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com").group(1)
'qq'
示例 \number
>>>import re
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h2></html>")
第二個(gè)匹配不到是后面的\2
引用了分組2的值(h1
)灵疮,與字符串中的‘h2’不匹配织阅,所以返回None
示例:(?P<name>) (?P=name)
>>>import re
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h2></html>")
注意:
(?P<name>)和(?P=name)中的字母p大寫(xiě)
8. re模塊的高級(jí)用法
1.search()
我們?cè)谇懊嫣岬竭^(guò)match()方法是從字符串的開(kāi)頭開(kāi)始匹配,一旦開(kāi)頭不匹配震捣,那么整個(gè)匹配就失敗了荔棉。
import re
s='Hello World 123abc456'
result=re.match('World.*abc',s)
print(result)
result=re.search('World.*abc',s)
print(result)
運(yùn)行結(jié)果
None
<_sre.SRE_Match object; span=(6, 18), match='World 123abc'>
在這里我們有一個(gè)字符串闹炉,它是以Hello開(kāi)頭的,但是正則表達(dá)式我們是以World開(kāi)頭的润樱,整個(gè)正則表達(dá)式是字符串的一部分渣触,但是這樣匹配是失敗的,也就是說(shuō)只要第一個(gè)字符不匹配整個(gè)匹配就不能成功壹若。
所以match()
方法在我們?cè)谑褂玫臅r(shí)候需要考慮到開(kāi)頭的內(nèi)容嗅钻,所以在做匹配的時(shí)候并不那么方便,它適合來(lái)檢測(cè)某個(gè)字符串是否符合某個(gè)正則表達(dá)式的規(guī)則店展。
在這里就有另外一個(gè)方法search()
啊犬,它在匹配時(shí)會(huì)掃描整個(gè)字符串,然后返回第一個(gè)成功匹配的結(jié)果壁查,也就是說(shuō)觉至,正則表達(dá)式可以是字符串的一部分,在匹配時(shí)睡腿,search()
方法會(huì)依次掃描字符串语御,直到找到第一個(gè)符合規(guī)則的字符串,然后返回匹配內(nèi)容席怪,如果搜索完了還沒(méi)有找到应闯,那就返回None。
我們把上面的代碼中的match()方法修改成search()挂捻,這樣就得到了匹配結(jié)果莫湘。所以說(shuō),為了匹配方便滨溉,我們可以盡量使用search()方法重贺。
2.findall()
在前面我們說(shuō)了search()
方法的用法,它可以返回匹配正則表達(dá)式的第一個(gè)內(nèi)容声怔,但是如果我們想要獲取匹配正則表達(dá)式的所有內(nèi)容的話怎么辦态贤?這時(shí)就需要借助于findall()
方法了。
findall()
方法會(huì)搜索整個(gè)字符串然后返回匹配正則表達(dá)式的所有內(nèi)容醋火。
3.sub()
正則表達(dá)式除了提取信息悠汽,我們有時(shí)候還需要借助于它來(lái)修改文本,比如我們想要把一串文本中的所有數(shù)字都去掉芥驳,如果我們只用字符串的replace()
方法那就太繁瑣了柿冲,在這里我們就可以借助于sub()
方法。
示例
將匹配到的數(shù)字加1
import re
ret = re.sub(r"\d+", '998', "python = 997")
print(ret)
import re
def add(temp):
strNum = temp.group()
num = int(strNum) + 1
return str(num)
ret = re.sub(r"\d+", add, "python = 997")
print(ret)
ret = re.sub(r"\d+", add, "python = 99")
print(ret)
運(yùn)行結(jié)果
python = 998
python = 100
可以傳一個(gè)函數(shù)進(jìn)去
4.split()
根據(jù)匹配進(jìn)行切割字符串兆旬,并返回一個(gè)列表
切割字符串“info:xiaoZhang 33 shandong”
>>>import re
>>>re.split(r":| ","info:xiaoZhang 33 shandong")
['info', 'xiaoZhang', '33', 'shandong']
5.compile()
前面我們所講的方法都是用來(lái)處理字符串的方法假抄,最后再介紹一個(gè)compile()方法,這個(gè)方法可以講正則字符串編譯成正則表達(dá)式對(duì)象,以便于在后面的匹配中復(fù)用慨亲。
例如這里有三個(gè)日期婚瓜,我們想分別將三個(gè)日期中的時(shí)間去掉,所以在這里我們可以借助于sub()方法刑棵,sub()方法的第一個(gè)參數(shù)是正則表達(dá)式巴刻,但是這里我們沒(méi)有必要重復(fù)寫(xiě)三個(gè)同樣的正則表達(dá)式,所以可以借助于compile()函數(shù)將正則表達(dá)式編譯成一個(gè)正則表達(dá)式對(duì)象蛉签,以便復(fù)用胡陪。
示例
import re
time1='2017-6-15 11:30'
time2='2017-6-16 12:30'
time3='2017-6-17 11:30'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',time1)
result2 = re.sub(pattern,'',time2)
result3 = re.sub(pattern,'',time3)
print(result1)
print(result2)
print(result3)
運(yùn)行結(jié)果
2017-6-15
2017-6-16
2017-6-17
另外compile()還可以傳入修飾符,例如re.S等修飾符碍舍,這樣在search()柠座、findall()等方法中就不需要額外傳了。所以compile()方法可以說(shuō)是給正則表達(dá)式做了一層封裝片橡,以便于我們更好地復(fù)用妈经。
9. 貪婪和非貪婪
Python里數(shù)量詞默認(rèn)是貪婪的(在少數(shù)語(yǔ)言里也可能是默認(rèn)非貪婪),總是嘗試匹配盡可能多的字符捧书;
非貪婪則相反吹泡,總是嘗試匹配盡可能少的字符。
在"*","?","+","{m,n}"
后面加上经瓷?
爆哑,使貪婪變成非貪婪。
練習(xí)代碼
import re
s = "This is a number 234-235-22-423"
r1 = re.match("(.+)(\d+-\d+-\d+-\d+)", s)
print('r1...')
print(r1.groups())
r2 = re.match("(.+?)(.*?)(\d+-\d+-\d+-\d+?)", s)
print('r2...')
print(r2.groups())
r3 = re.match("(.+)(.+?)(\d+-\d+-\d+-\d+?)", s)
print('r3...')
print(r3.groups())
r4 = re.match("(.+)(.*?)(\d+?)(-\d+-\d+-\d+?)", s)
print('r4...')
print(r4.groups())
r5 = re.match("(.+?)(.*)(\d+?)(-\d+-\d+-\d+?)", s)
print('r5...')
print(r5.groups())
分析一波代碼
r1(.+)
+ 匹配前一個(gè)字符出現(xiàn)1次或者無(wú)限次舆吮,即最少有一次 從第一個(gè)開(kāi)始匹配揭朝,并貪婪的盡可能多的匹配,將23也匹配上色冀,給下一個(gè)(\d+)
留一個(gè)4潭袱,因?yàn)?code>(\d+)至少匹配一個(gè)
r2 (.+?)
從第一個(gè)開(kāi)始匹配,并 非貪婪的匹配到一個(gè)T
呐伞,(.*?)
接著上個(gè)表達(dá)式繼續(xù)匹配敌卓,匹配到2
的時(shí)候發(fā)現(xiàn)(\d+-\d+-\d+-\d+?)
符合匹配,就結(jié)束匹配得到自己的his is a number
r4 (.+)
貪婪的匹配伶氢,發(fā)現(xiàn)下一個(gè)正則表達(dá)式(.*)
可有可無(wú),于是就接著匹配到(\d+?)
然后(.*)
這個(gè)組就是空瘪吏。
運(yùn)行結(jié)果
r1...
('This is a number 23', '4-235-22-423')
r2...
('T', 'his is a number ', '234-235-22-4')
r3...
('This is a number 2', '3', '4-235-22-4')
r4...
('This is a number 23', '', '4', '-235-22-4')
r5...
('T', 'his is a number 23', '4', '-235-22-4')
10. 練習(xí)
一癣防、從下面的字符串中取出文本(將標(biāo)簽去掉)
import re
s= '''<div>
<p>崗位職責(zé):</p>
<p>完成推薦算法、數(shù)據(jù)統(tǒng)計(jì)掌眠、接口蕾盯、后臺(tái)等服務(wù)器端相關(guān)工作</p>
<p><br></p>
<p>必備要求:</p>
<p>良好的自我驅(qū)動(dòng)力和職業(yè)素養(yǎng),工作積極主動(dòng)蓝丙、結(jié)果導(dǎo)向</p>
<p> <br></p>
<p>技術(shù)要求:</p>
<p>1级遭、一年以上 Python 開(kāi)發(fā)經(jīng)驗(yàn)望拖,掌握面向?qū)ο蠓治龊驮O(shè)計(jì),了解設(shè)計(jì)模式</p>
<p>2挫鸽、掌握HTTP協(xié)議说敏,熟悉MVC、MVVM等概念以及相關(guān)WEB開(kāi)發(fā)框架</p>
<p>3丢郊、掌握關(guān)系數(shù)據(jù)庫(kù)開(kāi)發(fā)設(shè)計(jì)盔沫,掌握 SQL,熟練使用 MySQL/PostgreSQL 中的一種<br></p>
<p>4枫匾、掌握NoSQL架诞、MQ,熟練使用對(duì)應(yīng)技術(shù)解決方案</p>
<p>5干茉、熟悉 Javascript/CSS/HTML5谴忧,JQuery、React角虫、Vue.js</p>
<p> <br></p>
<p>加分項(xiàng):</p>
<p>大數(shù)據(jù)沾谓,數(shù)理統(tǒng)計(jì),機(jī)器學(xué)習(xí)上遥,sklearn搏屑,高性能,大并發(fā)粉楚。</p>
</div>'''
a=re.sub('</?p>|</?div>|<br>| ','',s)
print(a)
運(yùn)行結(jié)果
崗位職責(zé):
完成推薦算法辣恋、數(shù)據(jù)統(tǒng)計(jì)、接口模软、后臺(tái)等服務(wù)器端相關(guān)工作
必備要求:
良好的自我驅(qū)動(dòng)力和職業(yè)素養(yǎng)伟骨,工作積極主動(dòng)、結(jié)果導(dǎo)向
技術(shù)要求:
1燃异、一年以上Python開(kāi)發(fā)經(jīng)驗(yàn)携狭,掌握面向?qū)ο蠓治龊驮O(shè)計(jì),了解設(shè)計(jì)模式
2回俐、掌握HTTP協(xié)議逛腿,熟悉MVC、MVVM等概念以及相關(guān)WEB開(kāi)發(fā)框架
3仅颇、掌握關(guān)系數(shù)據(jù)庫(kù)開(kāi)發(fā)設(shè)計(jì)单默,掌握SQL,熟練使用MySQL/PostgreSQL中的一種
4忘瓦、掌握NoSQL搁廓、MQ,熟練使用對(duì)應(yīng)技術(shù)解決方案
5、熟悉Javascript/CSS/HTML5境蜕,JQuery蝙场、React、Vue.js
加分項(xiàng):
大數(shù)據(jù)粱年,數(shù)理統(tǒng)計(jì)售滤,機(jī)器學(xué)習(xí),sklearn逼泣,高性能趴泌,大并發(fā)。