正則表達(dá)式
字符串是編程時遇到的最多的一種數(shù)據(jù)結(jié)構(gòu),比如判斷一個電子郵件地址或一個座機(jī)號碼是否符合格式要求,雖然我們可以提取 @ 前后的字符串,再分別判斷單詞和域名,但這樣代碼十分復(fù)雜, 而且難以復(fù)用。
這時,我們可以
創(chuàng)建一個正則表達(dá)式
用這個正則表達(dá)式去檢測字符串是否合法
正則表達(dá)式(Regulation Expression)是一種文本模式,包括普通字符(例如,a 到 z之間的字母,數(shù)字)和特殊字符(稱為元字符,具有圖書意義的字符,如 +表示其前導(dǎo)字符至少出現(xiàn)1次,$、*、())
基本用法
正則表達(dá)式本身也是個字符串,怎么用字符描述字符呢?這就是我們這篇文章要講的主要內(nèi)容。
直接給出字符,就是精確匹配:
\d 表示一個數(shù)字
\w 表示一個字母或數(shù)字
. 表示任意字符,若要表示 . 英文句號,需要加轉(zhuǎn)義字符 \.
因此:
00\d 可以匹配 003、004,但無法匹配 00A
\w\w\w 可以匹配 010
py. 可以匹配 pya、py3、py!
怎么匹配變長字符串:
*表示任意個字符(包括0個),如runoo*b可以匹配runob、runoob、runooob+表示至少出現(xiàn)一次?表示出現(xiàn) 0 次或 1 次{n,m}表示 n-m 個字符,注意:兩個數(shù)字 n 和 m 之間不能有空格,只用逗號分隔
所以,\d{3}\s+\d{3,8} 可以匹配任意用空格隔開的帶區(qū)號的電話號碼
但是,以上方法匹配 010 - 2569888 還是不可以的,需要正則的高級用法
進(jìn)階
當(dāng)有多種類型字符選擇時,如數(shù)字、字母、大小寫,可以用 [] 來將其包括進(jìn)去
[0-9a-zA-Z\_]可以匹配一個數(shù)字、字母、下劃線[0-9a-zA-Z\_]+可以匹配至少由一個數(shù)字、字母、下劃線組成的字符串,如0558、python343[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下劃線開頭的字符串,也是 python 合法變量格式[a-zA-Z\_][0-9a-zA-Z\_]{1,19}可以精確限制了字符串的長度是 1-20A|B匹配A或B,所以(P|p)ython可以匹配python或Python^表示行的開頭,^\d表示以數(shù)字開頭$表示行尾,\w$表示以字母結(jié)尾
^、$有什么作用,py 是可以匹配 python的,但 ^py$ 就只能匹配 py了
python的 re 模塊
python 內(nèi)置了 re 正則模塊,
現(xiàn)在嘗試寫一個正則表達(dá)式以驗(yàn)證 Email 地址:
Email 地址格式要求:
name@domain
name最長64,domain最長253,總長最長256
name可以使用任意ASCII字符:
大小寫英文字母 a-z,A-Z
數(shù)字 0-9
字符 !#$%&'*+-/=?^_`{|}~
字符 .不能是第一個和最后一個,不能連續(xù)出現(xiàn)兩次
但是有些郵件服務(wù)器會拒絕包含有特殊字符的郵件地址
domain僅限于26個英文字母、10個數(shù)字、連詞號-
連詞號-不能是第一個字符
頂級域名(com、cn等)長度為2到6個
代碼如下:
>>> r = r'^[a-zA-Z]+[\.\_]?[a-zA-Z0-9]+@[a-zA-Z0-9]([\_]?[a-zA-Z0-9]+)*\.[a-zA-Z]{2,6}(\.[a-zA-Z]{2})?$'
>>> re.match(r, 'simonkindle@126.com')
<_sre.SRE_Match object; span=(0, 19), match='simonkindle@126.com'>
>>> re.match(r, 'simonkindle@126.com.cn')
<_sre.SRE_Match object; span=(0, 22), match='simonkindle@126.com.cn'>
>>> re.match(r, 'simonkindle@126.com.cn.cn')
貪婪匹配
*、+ 都是貪婪匹配的,即在符合格式要求的情況下盡可能多地匹配文字。如果想取消貪婪匹配,在它們后面加上一個 ? 即可。
具體看下面例子:
>>> todo = `<H1>Chapter 1 - 介紹正則表達(dá)式</H1>`
>>> r = '<.*>'
>>> re.match(r, todo)
<_sre.SRE_Match object; span=(0, 28), match='<H1>Chapter 1 - 介紹正則表達(dá)式</H1>'>
>>> r = '<.*?>'
>>> re.match(r, todo)
<_sre.SRE_Match object; span=(0, 4), match='<H1>'>
前者貪婪匹配,匹配 < 與 > 之間盡可能多的字符,后者非貪婪匹配,匹配 < 與 > 之間盡可能少的字符。
切分字符串
正則表達(dá)式還可以用來切分字符串,如何將以不定數(shù)空格分割的單詞提取出來?
>>> s = 'a b fef v'
>>> s.split(' ')
['a', '', 'b', 'fef', '', '', 'v']
無法識別連續(xù)空格
>>> r = '[\s]+'
>>> re.split(r, s)
['a', 'b', 'fef', 'v']
分組
正則表達(dá)式還有提取子字符串的功能。用 () 表示的就是要提取的分組(Group)。
>>> r = '^(\d{3})-(\d{3,8})$'
>>> m = re.match(r, '022-22820279')
>>> m.groups()
('010', '22820279')
>>> m[0]
'010-22820279'
>>> m[1]
'010'
>>> m[2]
'22820279'
>>> m[3]
group(0)永遠(yuǎn)是原字符串,之后依次是括號中的字串
編譯
當(dāng)我們在 python 中使用正則表達(dá)式時,re 模塊會在背后干這樣兩件事:
編譯正則表達(dá)式,如果字符串本身不合法,會報(bào)錯
用編譯后的正則表達(dá)式去匹配字符串
如果我們使用的正則表達(dá)式要匹配上千次,每次都要編譯會浪費(fèi)大量時間。出于效率考慮,我們可以預(yù)編譯該表達(dá)式,這樣以后匹配就省去這個步驟了
>>> r = re.compile('^(\d{3})-(\d{3,8})$')
>>> r.match('010-22820279')
<_sre.SRE_Match object; span=(0, 12), match='010-22820279'>
>>> r.match('010-22820279').groups()
('010', '22820279')