一. 簡(jiǎn)介
正則表達(dá)式本身是一種小型的,高度專業(yè)的編程語(yǔ)言,二在Python中,通過(guò)內(nèi)嵌集成re 模塊,我們可以直接調(diào)用來(lái)實(shí)現(xiàn)正則匹配.正的表達(dá)式模式被編譯成一些列的字節(jié)碼,然后有C編寫(xiě)的匹配引擎執(zhí)行.
正則表達(dá)式是一種字符串搜索和匹配的工具.
友情鏈接:鬼斧神工之正表達(dá)式
二. 正則表達(dá)式中常用的字符串
1. 普通字符和11個(gè)元字符:
普通字串 | 匹配自身 | abc | abc |
---|---|---|---|
. | 匹配任意除換行符"\n"外的字符(在DOTALL模式中也能匹配換行符 | a.b | abc |
\ | 轉(zhuǎn)義字符,使有一個(gè)字符改變?cè)瓉?lái)的意思 | a\.c;a\\c | a.c;a\c |
* | 匹配前一個(gè)字符0或多次 | abc* | ab;abccc |
+ | 匹配前一個(gè)字符1次或無(wú)線次 | abc+ | abc;abccc |
? | 匹配一個(gè)字符0次或1次 | abc? | ab;abc |
^ | 匹配字符串開(kāi)頭.在多行匹配每一行的開(kāi)頭 | ^abc | abc |
$ | 匹配字符末尾,在多行匹配模式中匹配每一行的末尾 | abc$ | abc |
| | 或. 匹配|左右表達(dá)式任意一個(gè),從左到右匹配,如果|沒(méi)有包括在()中,則它的范圍是整個(gè)正則表達(dá)式 | abc|def | abc def |
{} | {m}匹配前一個(gè)字符m次,{m,n}匹配前一個(gè)字符m至n次,若省略n,則匹配m至無(wú)限次 | ab{1,2}c | abc abbc |
[] | 字符集.對(duì)應(yīng)的位置可以是字符集中任意字符.字符集中的字符的字符可以逐個(gè)列出,也可以給出范圍,如[abc]或[a-c],[^abc]表示取反,即非abc,所有特殊字符在字符集中都失去其原有的特殊含義.用\反斜杠轉(zhuǎn)義恢復(fù)特殊字符的特殊含義 | a[bcd]e | abe ace ade |
() | 被括起來(lái)的表達(dá)式將作為分組,從表達(dá)式左邊開(kāi)始沒(méi)沒(méi)遇到一個(gè)分組的左括號(hào)"(",編號(hào)+1.分組表達(dá)式作為一個(gè)整體,可以后接數(shù)量詞.表達(dá)式中的|僅在該組中有效. | (abc){2}/a(123|456)c | abcabc/a456c |
這里需要強(qiáng)調(diào)一下反斜杠\的作用
- 反斜杠后邊根元字符去除特殊功能;(即將特殊字符轉(zhuǎn)移成普通字符)
- 反斜杠后邊跟普通字符實(shí)現(xiàn)特殊功能;(即預(yù)定義字符)
- 引用序號(hào)對(duì)應(yīng)的字組所匹配的字符串.
import re
a = re.search(r'(tina)(fei)haha\2','tinafeihahafei tinafihahatina').group()
print("結(jié)果:",a)
================
結(jié)果: tinafeihahafei
2. 預(yù)定義字符集(可以寫(xiě)在字符集[...]中)
字符集 | 含義 | 示例 | 結(jié)果 |
---|---|---|---|
\d | 數(shù)字:[0-9] | a\dc | a1c |
\D | 非數(shù)字:[^\d] | a\Dc | abc |
\s | 匹配任何空白字符:[<空格>\t\r\n\f\v] | a\sc | a c |
\S | 非空白字符:[^\s] | a\Sc | abc |
\w | 匹配包括下劃線在內(nèi)的任何字符(數(shù)字,字母,下劃線):[A-Za-z0-9_] | a\wc | abc |
\W | 匹配非字符字母字符,即匹配特殊字符 | a\Wc | a c |
\A | 僅匹配字符串開(kāi)頭,同^ | \Aabc | abc |
\Z | 僅匹配字符串結(jié)尾,同$ | abc\Z | abc |
\b | 匹配\w和\W之間,即匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置.例如,'er\b'可以匹配"never"中的'er',但不能匹配"verb"中的'er'. | \babc\b a\b!bc | 空格abc空格 a!bc |
\B | [^\b] | a\Bbc | abc |
# 這里強(qiáng)調(diào)一下\b的單詞邊界的理解:
import re
w = re.findall('\btina','tian tinaaaa')
print("結(jié)果是:",w)
s = re.findall(r'\btina','tian tinaaaa')
print("結(jié)果是:",s)
v = re.findall(r'\btina','tian#tianaaaa')
print("結(jié)果是:",v)
a = re.findall(r'\btina\b','tian#tina@aaa')
print("結(jié)果是:",a)
==================
結(jié)果是: []
結(jié)果是: ['tina']
結(jié)果是: ['tina']
結(jié)果是: ['tina']
3. 特殊分組用法:
特殊分組 | 含義 | 用法示例 | 結(jié)果 |
---|---|---|---|
(?P<name>) | 分組,除了原有的編號(hào)外再指定一個(gè)額外的別名 | (?P<id>abc){2} | abcabc |
(?P=name) | 引用別名為<name>的分組匹配到字符串 | (?P<id>\d)abc(?P=id) | 1abc1 5abc5 |
<number> | 引用編號(hào)為<number>的分組匹配到字符串 | (\d)abc\1 | 1abc1 5abc5 |
三. re模塊中常用的功能函數(shù)
1. compile()
編譯正則表達(dá)式模式,返回一個(gè)對(duì)象的模式.(可以吧那些常用的正則表達(dá)式編譯成正則表達(dá)式對(duì)象,這樣可以提高效率.)
-
格式:
re.compile(pattern,flags=0)
pattern:編譯時(shí)用的表達(dá)式字符串
flags 編譯標(biāo)志位,用于修改正則表達(dá)式的匹配方式,如:是否區(qū)分大小寫(xiě),多行匹配等.常用的flags有:
標(biāo)志 | 含義 |
---|---|
re.S(DOTALL) | 使.匹配包括換行符在內(nèi)的所有字符 |
re.I (IGNOTECASE) | 使匹配對(duì)大小寫(xiě)不敏感 |
re.L (LOCALE) | 做本地化識(shí)別(local-aware匹配,語(yǔ)法等) |
re.M(MULTILINE) | 多行匹配,影響^和$ |
re.X(VERBOSE) | 該標(biāo)志通過(guò)給予更靈活的格式寫(xiě)得更易于理解 |
re.U | 根據(jù)Unicode字符集解析字符,這個(gè)標(biāo)志影響\w,\W,\b,\B |
import re
tt = "Tom is a good boy, he is cool,clever,and so on ..."
rr = re.compile(r'\w*oo\w*')
print("執(zhí)行結(jié)果是:",rr.findall(tt)) # 查找所有包含'oo'的單詞
================
執(zhí)行結(jié)果是: ['good', 'cool']
2. match()
決定RE是否在字符串剛開(kāi)始的位置匹配. //注:
這個(gè)方法并不是完全匹配.當(dāng)pattern結(jié)束時(shí)若string還有剩余字符,仍然是為成功.想要完全匹配,可以在表達(dá)式末尾加上邊界匹配字符'$'
- 格式"
re.match(pattern,stting,flags=0)
import re
a = re.match('com','comwww.runcomoob').group()
print("a的結(jié)果是:",a)
b = re.match('com','Comwww.baidu',re.I)
print("b的結(jié)果是:",b) # re.I 對(duì)大小寫(xiě)不敏感
========================
a的結(jié)果是: com
b的結(jié)果是: Com
3. search()
re.search() 函數(shù)會(huì)在字符串內(nèi)查找模式匹配,只要找到第一個(gè)匹配然后返回,如果沒(méi)有找到匹配,則返回None.
- 格式:
re.search(pattern,strint,flags=0)
import re
a = re.search('\dcom','www.4comrnuoob.5com').group()
print("a的結(jié)果是:",a)
b = re.search('com','python.org')
print("b的結(jié)果是:",b)
===================
a的結(jié)果是: 4com
b的結(jié)果是: None
-
注: match和search一旦匹配成功,就是一個(gè)match object對(duì)象,而match object對(duì)象有以下方法:
- group() 返回被RE匹配的字符串
- start() 返回匹配開(kāi)始的位置
- end() 返回匹配結(jié)束的位置
- span() 返回一個(gè)元組包含匹配(開(kāi)始,結(jié)束)的位置.
- group() 返回re整體匹配的字符串,可以一次輸入多個(gè)組號(hào),對(duì)應(yīng)組號(hào)匹配的字符串.
a.group() 返回re整體匹配的字符串
b.group(n,m) 返回組號(hào)為n,m所匹配的字符串,如果不存在,則返回indexError異常
c.group() group方法返回一個(gè)包含正則表達(dá)式中所有小組字符串的元組,從1到所含的小組號(hào),通常group()不需要參數(shù),返回一個(gè)元組,元組中的元就是正則表達(dá)式中定義的組.
import re
a = "123abc456"
r = re.search('([0-9]*)([a-z]*)([0-9]*)',a)
print("r.group(0)的結(jié)果",r.group(0))
print("r.group(1)的結(jié)果",r.group(1))
print("r.group(2)的結(jié)果",r.group(2))
print("r.group(3)的結(jié)果",r.group(3))
#group(1) 列出第一個(gè)括號(hào)匹配部分初斑,group(2) 列出第二個(gè)括號(hào)匹配部分,group(3) 列出第三個(gè)括號(hào)匹配部分日川。
=======================
r.group(0)的結(jié)果 123abc456
r.group(1)的結(jié)果 123
r.group(2)的結(jié)果 abc
r.group(3)的結(jié)果 456
4 . finfall()
re.findall 遍歷匹配,可以獲取字符串中所有的字符串,返回一個(gè)列表.
-
格式:
re.findall(pattern,string,flags=0)
import re
p = re.compile(r'\d+')
r = p.findall('o1n2m3k4')
print("匹配的結(jié)果是:",r)
============
匹配的結(jié)果是: ['1', '2', '3', '4']
import re
tt = "Jack is a good boy, he is cool,clever,and so on .... "
rr = re.compile(r'\w*oo\w*')
print("rr.findall(tt)匹配的結(jié)果是:",rr.findall(tt))
print("re.findall(r'(\w)*oo(\w)')匹配的結(jié)果是:",re.findall(r'(\w)*oo(\w)',tt))
==================
rr.findall(tt)匹配的結(jié)果是: ['good', 'cool']
re.findall(r'(\w)*oo(\w)')匹配的結(jié)果是: [('g', 'd'), ('c', 'l')]
5. finditer()
搜索string,返回一個(gè)順序訪問(wèn)每一個(gè)匹配結(jié)果(Match對(duì)象)的迭代器.找到RE匹配的所有字符串,并把他們作為一個(gè)迭代器返回.
-
格式:
re.finditer(pattern,strint,flags=0)
import re
iter = re.finditer(r'\d+','12 drumm44ers drumming,11 ... 10 ... ')
for i in iter:
print("匹配結(jié)果是:",i)
print("i.group()匹配結(jié)果是:",i.group())
print("i.span()匹配結(jié)果是:",i.span())
==========================================
匹配結(jié)果是: <_sre.SRE_Match object; span=(0, 2), match='12'>
i.group()匹配結(jié)果是: 12
i.span()匹配結(jié)果是: (0, 2)
匹配結(jié)果是: <_sre.SRE_Match object; span=(8, 10), match='44'>
i.group()匹配結(jié)果是: 44
i.span()匹配結(jié)果是: (8, 10)
匹配結(jié)果是: <_sre.SRE_Match object; span=(23, 25), match='11'>
i.group()匹配結(jié)果是: 11
i.span()匹配結(jié)果是: (23, 25)
匹配結(jié)果是: <_sre.SRE_Match object; span=(30, 32), match='10'>
i.group()匹配結(jié)果是: 10
i.span()匹配結(jié)果是: (30, 32)
6. split()
按照能夠匹配的字符串將string分割后返回列表.
可以使用re.split來(lái)分割字符串,如:re.split(r'\s','text');將字符串按空格分割成一個(gè)單詞列表.
-
格式:
re.split(pattern,string[,maxsplit])
maxsplit用于指定最大分割次數(shù),不指定將全部分割.
import re
rr = re.split('\d+','one1two2three3four4five5')
print("rr匹配的結(jié)果是:",rr)
wrr = re.split('[a-z]+','one1two2three3four4five5')
print("wrr匹配的結(jié)果是:",wrr)
================================
rr匹配的結(jié)果是: ['one', 'two', 'three', 'four', 'five', '']
wrr匹配的結(jié)果是: ['', '1', '2', '3', '4', '5']
7. sub()
使用re替換string中每一個(gè)匹配的字符串后返回替換后的字符串.
-
格式:
re.sub(pattern,repl,string,count)
repl:是用什么字符串替換(替換后的字符串)
count:指的是替換的個(gè)數(shù),默認(rèn)為0,表示每個(gè)都替換.
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
rr = re.sub(r'\s+','-',text)
print("替換后的結(jié)果是:",rr)
========================
替換后的結(jié)果是: JGood-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...
re.sub還允許使用函數(shù)對(duì)匹配項(xiàng)的替換進(jìn)行復(fù)雜的處理.
如:re.sub(r'\s',lambda m:'['+ m.group(0) +']',text,0);將字符串中的空格' '替換為'[]'.
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
rr = re.sub(r'\s',lambda m:'['+ m.group(0) +']',text,0)
print("替換后的結(jié)果是:",rr)
===========================
替換后的結(jié)果是: JGood[ ]is[ ]a[ ]handsome[ ]boy,[ ]he[ ]is[ ]cool,[ ]clever,[ ]and[ ]so[ ]on...
8. subn()
返回替換次數(shù)
- 格式:
import re
tt = '123456abcdef'
rr = re.subn('[1-2]','A',tt)
rr1 = re.sub("g.t","have",'I get A,I get B,I get C')
rr2 = re.subn("g.t","have",'I get A,I get B,I get C')
=====================
re.subn()匹配的結(jié)果: ('AA3456abcdef', 2)
re.sub()匹配的結(jié)果: I have A,I have B,I have C
re.subn()匹配的結(jié)果: ('I have A,I have B,I have C', 3)
四. 一些注意點(diǎn)
1. re.match與re.search與re.findall的區(qū)別:
re.match只匹配字符串的開(kāi)始,如果字符串開(kāi)始不符合正則表達(dá)式,則匹配失敗,函數(shù)返回None;而re.search匹配整個(gè)字符串,知道找到一個(gè)匹配.
import re
a = re.search('[\d]','abc33').group()
print("a匹配的結(jié)果是:",a)
p = re.match('[\d]','abc33')
print("p匹配的結(jié)果是:",p)
b = re.findall('[\d]','abc33')
print("b匹配的結(jié)果是:",b)
============================
a匹配的結(jié)果是: 3
p匹配的結(jié)果是: None
b匹配的結(jié)果是: ['3', '3']
2. 貪婪匹配與非貪婪匹配
?,+?,??,{m,n}? 前面的,+,?等都是貪婪匹配,也就是盡可能匹配,后面加?號(hào)使其邊城惰性匹配
- 示例一
import re
a = re.findall('a(\d+?)','a23b')
print("a惰性匹配的結(jié)果:",a)
b = re.findall('a(\d+)','a23b')
print("b貪戀匹配的結(jié)果:",b)
==========================
a惰性匹配的結(jié)果: ['2']
b貪戀匹配的結(jié)果: ['23']
- 示例二
import re
a = re.match('<(.*)>','<H1>title</H1>').group()
print("a是貪婪匹配的結(jié)果:",a)
b = re.match('<(.*?)>','<H1>title</H1>').group()
print("b是惰性匹配的結(jié)果:",b)
==============================
a是貪婪匹配的結(jié)果: <H1>title</H1>
b是惰性匹配的結(jié)果: <H1>
- 示例三
這里需要注意的是如果前后均有限定條件的時(shí)候,就不存在什么貪婪模式了琐谤,非匹配模式失效赡模。
import re
a = re.findall(r'a(\d+)b','a33333b')
print("a是貪婪匹配的結(jié)果:",a)
b = re.findall(r'a(\d+?)b','a33333b')
print("b是惰性匹配的結(jié)果:",b)
====================
a是貪婪匹配的結(jié)果: ['33333']
b是惰性匹配的結(jié)果: ['33333']
3. 用flags時(shí)遇到的小坑
import re
rr = re.split('a','1A1a2A3',re.I)
print("rr輸出的結(jié)果是:",rr)
================
rr輸出的結(jié)果是: ['1A1', '2A3']
輸出的結(jié)果冰未能區(qū)分大小寫(xiě),這是因?yàn)閞e.split(pattern,string杭跪,maxsplit,flags)默認(rèn)是四個(gè)參數(shù)通惫,當(dāng)我們傳入的三個(gè)參數(shù)的時(shí)候茂翔,系統(tǒng)會(huì)默認(rèn)re.I是第三個(gè)參數(shù),所以就沒(méi)起作用履腋。如果想讓這里的re.I起作用珊燎,寫(xiě)成flags=re.I即可惭嚣。
import re
rr = re.split('a','1A1a2A3',flags=re.I)
print("rr輸出的結(jié)果是:",rr)
=================
rr輸出的結(jié)果是: ['1', '1', '2', '3']
五. 正則匹配的小實(shí)踐
1. 匹配電話號(hào)碼
import re
# 方法一:
phone_pat = re.compile('^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$')
while True:
phone = input('請(qǐng)輸入你的電話號(hào)碼:')
res = re.search(phone_pat,phone)
if res:
print('正常手機(jī)號(hào)')
else:
print('不是手機(jī)號(hào)')
# 方法二
tel_pat = re.compile('^0\d[0-9]{2}|[0-9]{3}-\d[0-9]{8}|[0-9]{7}')
while True:
tel = input('請(qǐng)輸入你的座機(jī)號(hào):')
res = re.search(tel_pat,tel)
# res1 = re.search(tel_pat1,tel)
if res:
print('格式正確')
else:
print('格式不正確')
2. 匹配IP:
import re
re.search(r"(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5]\.)","192.168.1.1")
3. 匹配郵箱:
import re
text = input("Please input your Email address:\n")
if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$',text):
#if re.match(r'[0-9a-zA-Z_]{0,19}@163.com',text):
print('Email address is Right!')
else:
print('Please reset your right Email address!')