1. 程式人生 > >正則表示式(二)——進階之匹配分組

正則表示式(二)——進階之匹配分組

這部分我將換種形式來講述,結合實際問題需求來講述。

3.匹配分組

字元 功能
| 匹配左右任意一個表示式
(ab) 將括號中的字元作為一個分組
\num 引用分組num匹配到的字串
(?P<name>) 分組起別名
(?P=name) 引用別名為name分組匹配到的字串

1)| 

需求:匹配出0-100之間的數字

分析:首先我們明確這之間的數字包含0,兩位數,100,,也就是一位,兩位,三位的可能都有,鑑於一位0,三位100都是單獨數字,我們可以使用 | 來連線(相當於or),著重考慮一位數[1-9],兩位數[10-99],分析其特徵,這裡第一位數必須為[1-9],第二位[0-9]都可以(\d可以表示),對於一位數[1-9]的第二位可以不出現(?可以表示),再加個結尾$,防止234匹配到23的情況。因此正則表示式為:

r'[1-9]\d?$|0$|100$'
>>> re.match(r'[1-9]\d?$|0$|100$','08')
None
>>> re.match(r'[1-9]\d?$|0$|100$','0')
<_sre.SRE_Match object; span=(0, 1), match='0'>
>>> re.match(r'[1-9]\d?$|0$|100$','100')
<_sre.SRE_Match object; span=(0, 3), match='100'>
>>> re.match(r'[1-9]\d?$|0$|100$','101')
None
>>> re.match(r'[1-9]\d?$|0$|100$','80')
<_sre.SRE_Match object; span=(0, 2), match='80'>

我們再看下0能不能不用 | ,也把它包含進去?答案of course。想想 0 ,它是[1-9]不出現,第二位\d也不出現的情況,因此第一位[1-9]選擇性出現用 ? ,因此正則表示式為:

r'[1-9]?\d?$|100$'
>>> re.match(r'[1-9]?\d?$|100$','0')
<_sre.SRE_Match object; span=(0, 1), match='0'>

這樣的話,| 的功能是不是清楚了......

 2)(ab) 將括號中的字元作為一個分組

需求 :在網頁開發或者爬蟲時,處理物件是HTML,比如某網頁HTML中有這樣的內容:<h1>我的CSDN</h1>。那麼我該如何把<h1>content</h1>中的content提取出來呢?

分析:提取的內容用()括起來就可以了。<h1> </h1>是固定標籤,這是不變的,變的是content,因此我的正則表示式可表示為:

r'<h1>(.*)</h1>'
>>> re.match(r'<h1>(.*)</h1>','<h1>我的CSDN</h1>')
<_sre.SRE_Match object; span=(0, 15), match='<h1>我的CSDN</h1>'>

可以完美的匹配到了,問題還沒解決,我怎麼提取出來content呢。其實group()函式是可以接受引數的,其引數的意義是你的()個數的索引,比如你輸入reslut.group(1)得到的是第一個括號的內容。reslut.group(0)是匹配到的所有內容,預設為0,所以你不輸入,預設為0,返回匹配內容。Match Object還有個groups()函式,它返回的是所有括號的內容。如下所示:

>>> result = re.match(r'<h1>(.*)</h1>','<h1>我的CSDN</h1>')  #變數儲存
>>> result.group()  #預設0,返回匹配內容
'<h1>我的CSDN</h1>'
>>> result.group(0) #輸入0,返回匹配內容
'<h1>我的CSDN</h1>'
>>> result.group(1)  #返回第1個括號內容
'我的CSDN'
>>> result.groups()
('我的CSDN',)

我們看看兩個括號的情況:

>>> result = re.match(r'<span>(\d+)</span><h1>(.*)</h1>','<span>1234</span><h1>我的CSDN</h1>')
>>> result
<_sre.SRE_Match object; span=(0, 32), match='<span>1234</span><h1>我的CSDN</h1>'>
>>> result.group(1)
'1234'
>>> result.group(2)
'我的CSDN'
>>> result.groups()
('1234', '我的CSDN')

所以說,()在網頁開發,爬蟲裡還是挺重要的......學會它

 3)\num 引用分組num匹配到的字串

需求 :還是以網頁為例,比如判斷某網頁HTML格式是否正確,其中有內容:<html><h1>我的CSDN</h1></html>。這時,我們不關心裡面的內容,我們關心的是格式是否正確,就是說這樣的格式:<html>標籤必須有</html>結束,<h1>標籤必須有</h1>結束。

分析:這個時候像html和h1才是關鍵內容,如果我們把這些用()儲存起來,並且在後面一定對應這些內容,就能保證標籤的配對,而\num就是解決這樣的問題的,上面group裡面的引數index指的是第index個括號內容,\index即取得它的內容

1. 首先我們看個錯誤的樣例:

>>> s = '<html><h1>my csdn</h1></html>'
>>> re.match(r'<.+><.+>.+</.+></.+>',s)   #關注html和h1這些內容, .+ 表示至少有內容
<_sre.SRE_Match object; span=(0, 29), match='<html><h1>my csdn</h1></html>'>
>>> s = '<html><h1>my csdn</h1></h>'    '''把s後面的html改成h,明顯格式不對,但還是匹配了'''
>>> re.match(r'<.+><.+>.+</.+></.+>',s)
<_sre.SRE_Match object; span=(0, 26), match='<html><h1>my csdn</h1></h>'>

我們應該讓第一個<tag>的內容出現在最後</tag>中,因此需要儲存起來,用()括起來

2.正確的方法

我們應該把<tag>用()括起來,變成<(tag)>,因此正則表示式為:

r'<(.+)><(.+)>.+</\2></\1>'

解釋下,這個\2 和 \1。這個就是我們上面groups()中的內容索引,可以拿到內容

>>> s = '<html><h1>my csdn</h1></h>'          #錯誤的標籤格式
>>> re.match(r'<(.+)><(.+)>.+</\2></\1>',s)   #此時不符合了
None

>>> s = '<html><h1>my csdn</h1></html>'       #正確的標籤格式
>>> result = re.match(r'<(.+)><(.+)>.+</\2></\1>',s)
>>> result
<_sre.SRE_Match object; span=(0, 29), match='<html><h1>my csdn</h1></html>'>
>>> result.groups()                     #那麼 \2 指的是'h1' ; \1 指的是'html' 
('html', 'h1')

4)(?P<name>) 分組起別名 和 (?P=name) 引用別名

需求 :上面我們已經可以通過\num取得對應的內容用以限定前面的內容,但是如果我有100個括號,那我們就只能數括號的索引index,然後把用\index取得麼?答案:當然不是。

分析:這時候取別名(?P<name>)就派上用場了,它通過對括號裡的內容取別名,然後通過(?P=name)就可以取得對應的內容,你只要記住別名name,這樣就不用去數括號,記索引了。是不是很人性化......

這個語法是:在括號裡面同時寫上取的別名,以<>命名;引用時以=取得別名,所以該正則表示式為:

r'<(?P<key1>.+)><(?P<key2>.+)>.+</(?P=key2)></(?P=key1)>'
>>> s = '<html><h1>my csdn</h1></h>'    #不正確的格式
>>> result = re.match(r'<(?P<key1>.+)><(?P<key2>.+)>.+</(?P=key2)></(?P=key1)>',s)
None
>>> s = '<html><h1>my csdn</h1></html>'  #正確的格式
>>> result = re.match(r'<(?P<key1>.+)><(?P<key2>.+)>.+</(?P=key2)></(?P=key1)>',s)
>>> result
<_sre.SRE_Match object; span=(0, 29), match='<html><h1>my csdn</h1></html>'>
>>> result.groups()
('html', 'h1')

當然此時,你使用\num也是可以的,記住:(?P<name>)只是起了個別名,系統儲存了別名,你不用也沒關係,\num還是可以用的。看下圖,還是可以用\1來表示第一個內容。

>>> s = '<html><h1>my csdn</h1></html>'
>>> result = re.match(r'<(?P<key1>.+)><(?P<key2>.+)>.+</(?P=key2)></\1>',s)
>>> result
<_sre.SRE_Match object; span=(0, 29), match='<html><h1>my csdn</h1></html>'>

先放個表格:

re模組中一些重要的函式
函式 描述
compile(pattern,flags= 0) 使用任何可選的標記來編譯正則表示式的模式,然後返回一個正則表示式物件
match(pattern,string,flags=0) 嘗試使用帶有可選的標記的正則表示式的模式來匹配字串。如果匹配成功,就返回匹配物件; 如果失敗,就返回 None
search(pattern,string,flags=0) 使用可選標記搜尋字串中第一次出現的正則表示式模式。 如果匹配成功,則返回匹配物件; 如果失敗,則返回 None
findall(pattern,string[, flags] ) 查詢字串中所有(非重複)出現的正則表示式模式,並返回一個匹配列表
finditer(pattern,string[, flags] ) 與 findall()函式相同,但返回的不是一個列表,而是一個迭代器。 對於每一次匹配,迭代器都返回一個匹配物件
split(pattern,string,max=0) 根據正則表示式的模式分隔符, split函式將字串分割為列表,然後返回成功匹配的列表,分隔最多操作 max 次(預設分割所有匹配成功的位置)
sub(pattern,repl,string,count=0) 使用 repl 替換所有正則表示式的模式在字串中出現的位置,除非定義 count, 否則就將替換所有出現的位置( 另見 subn()函式,該函式返回替換操作的數目)
purge() 清除隱式編譯的正則表示式模式