本節(jié)內容


  1. re模塊介紹

  2. 使用re模塊的步驟

  3. re模塊簡單應用示例

  4. 關于匹配對象的說明

  5. 說說正則表達式字符串前的r前綴

  6. re模塊綜合應用實例

  7. 參考文檔

提示: 由于該站對MARKDOWN的表格支持的不是很好,所以本文中的表格均以圖片的形式提供,大家如果看著比較模糊,可以放大來看或下載圖片在本地查看。

正則表達式(Regluar Expressions)又稱規(guī)則表達式,在代碼中常簡寫為REs,regexes或regexp(regex patterns)。它本質上是一個小巧的、高度專用的編程語言。 通過正則表達式可以對指定的文本實現
匹配測試、字串/內容查找、子串/內容替換、字符串分割 等功能。正則表達式的語法和使用不是本節(jié)要講的內容(關于正則表達式的詳細介紹請參考另一篇博文《正則表達式總結》),本節(jié)主要介紹的是Python中是如何使用re模塊來完成正則表達式的相關操作的。

一、re模塊介紹


Python中的re模塊提供了一個正則表達式引擎接口,它允許我們將正則表達式編譯成模式對象,然后通過這些模式對象執(zhí)行模式匹配搜索和字符串分割、子串替換等操作。re模塊為這些操作分別提供了模塊級別的函數以及相關類的封裝。

1. re模塊提供的類

Python中的re模塊中最重要的兩個類:

描述
Regular Expression Objects正則表達式對象,用于執(zhí)行正則表達式相關操作的實體
Match Objects正則表達式匹配對象,用于存放正則表達式匹配的結果并提供給用于獲取相關匹配結果的方法

正則表達式對象類中定義的方法屬性

通過re模塊的compile()函數編譯得到的正則表達式對象(下面用regex表示)支持如下方法:

電腦培訓,計算機培訓,平面設計培訓,網頁設計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

參數說明:
  • string: 要匹配或處理的字符串

  • pos: 可選參數,表示從string字符串的哪個位置開始,相當于先對字符串做切片處理string[pos:]

  • endpos: 可選參數,表示到string字符串的哪個位置結束(不包含該位置)

  • maxsplit: regex.split()方法的可選參數,表示最大切割次數;默認值為0,表示能切割多少次就盡可能多的切割多少次

  • count: regex.sub()和regex.subn()方法的可選參數,表示最大替換次數;默認為0,表示能替換多少次就盡可能多的替換多少次

  • repl: sub和subn函數中的repl表示replacement,用于指定將匹配到的子串替換成什么內容,需要說明的是該參數的值可以是一個字符串,也可以是一個函數

說明: 如果指定了pos和endpos參數,就相當于在進行正則處理之前先對字符串做切片操作 string[pos, endpos],如rx.search(string, 5, 50)就等價于rx.search(string[5:50]),也等價于rx.search(string[:50], 5);如果endpos比pos的值小,則不會找到任何匹配。

匹配對象類中定義的方法和屬性

調用正則表達式對象的regex.match()、regex.fullmatch()和regex.search()得到的結果就是一個匹配對象,匹配對象支持以下方法和屬性:

電腦培訓,計算機培訓,平面設計培訓,網頁設計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

參數說明:
  • template: m.expand()方法中的template參數是一個末班字符串,這個字符串中可以使用分組的數值后向引用(如:\1,\2)或命名后向引用(如\g<1>,\g<NAME>)來表示某個分組的占位符;m.expand()方法的執(zhí)行過程實際上就是通過sub()方法把template字符串中的這些分組占位符用當前匹配對象中的數據進行替換。

  • default: m.groups()與m.groupdict()方法中的default都是為為匹配成功的捕獲組提供默認匹配值的。

  • group: m.group()、m.start()、m.end()和m.span()方法中的group參數都表示要選擇的分組索引值,1表示第一個分組,2表示第二個分組,依次類推,group參數的默認值是0,表示整個正則表達式所匹配的內容。

2. re模塊提供的函數

re模塊提供了以下幾個模塊級別的函數

電腦培訓,計算機培訓,平面設計培訓,網頁設計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

參數說明:
  • pattern: 一個正則表達式對象(可以通過compile函數獲取)或一個表示正則表達式匹配模式的Python字符串,對于compile函數來說將pattern參數只能是字符串;

  • string: 需要用正則表達式來匹配的字符串對象

  • flags: 一個標志位,它會影響正則表達式對象的匹配行為,可取值下面會介紹;但是有一點需要說明的是,只有當pattern參數是字符串時才能指定這個flags參數,否則會報錯;如果pattren參數是一個正則表達式對象,則flags參數需要在調用re.compile()函數時指定。

  • repl: sub和subn函數中的repl表示replacement,用于指定將匹配到的子串替換成什么內容,需要說明的是該參數的值可以是一個字符串,也可以是一個函數

  • count: sub和subn函數中的count表示最多可替換次數

  • maxsplit: split函數中的maxsplit蠶食表示最大分隔次數

說明: 通過對比會發(fā)現,上面這些re模塊級別的函數除了re.compile、re.purge和re.escape這幾個函數外,其它函數名都與正則表達式對象支持的方法同名。實際上re模塊的這些函數都是對正則表達式對象相應方法的封裝而已,功能是相同的。只是少了對pos和endpos參數的支持,但是我們可以手動通過字符串切片的方式來達到相應的需求。個人認為,我們應該盡可能的使用模塊級別的函數,這樣可以增強代碼的兼容性。

3. 標志位flags

由上面的描述可知,flags參數在上面這些模塊函數中是要個可選參數,re模塊中預定了該參數可取的值:

電腦培訓,計算機培訓,平面設計培訓,網頁設計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

說明: 這些flag可以單獨使用,也可以通過邏輯或操作符'|'進行拼接來聯合使用。

二、使用re模塊的步驟


我們有必要對re模塊中所包含的類及其工作流程進行一下簡單的、整體性的說明,這講有利于我們對下面內容的理解。

1. 使用re模塊進行正則匹配操作的步驟

  • 1)編寫表示正則表達式規(guī)則的Python字符串str;

  • 2)通過re.compile()函數編譯該Python字符串獲得一個正則表達式對象(Pattern Object)p;

  • 3)通過正則表達式對象的p.match()或p.fullmatch()函數獲取匹配結果--匹配對象(Match Object)m;

  • 4)通過判斷匹配對象m是否為空可知是否匹配成功,也可以通過匹配對象m提供的方法獲取匹配內容。

2. 使用re模塊進行字串查找、字串替換和字符串分隔操作的步驟

  • 1)編寫表示正則表達式規(guī)則的Python字符串str;

  • 2)通過re.compile()函數編譯該Python字符串獲得一個正則表達式對象(Pattern Object)p;

  • 3)通過正則表達式對象的p.search()或p.findall()或p.finditer()或p.sub()或p.subn()或p.split()函數完字串查找、字串替換和字符串分隔操作并獲取相應的操作結果;

總結: 關于正則表達式的語法和編寫示例請參考《正則表達式總結》)。根據上面的描述可知,將一個表示正則表達式的Python字符串編譯成一個正則表達式對象是使用正則表達式完成相應功能的首要步驟,re模塊中用于完成正則表達式編譯功能的函數為re.compile()。

三、re模塊簡單應用示例


在上面的內容中,我們已經列出了re模塊所提供的類,以及這些類的對象所支持的函數和屬性,還有re模塊所提供的模塊級別的函數。這里我們要討論的是怎樣合理的使用這些方法和函數,換句話說就是在什么情況下使用這些方法和函數,以及如何使用的問題。我們之前說過,正則表達式主要可以用來提供以下幾個功能:

  • 匹配測試

  • 子串/內容查找

  • 子串/內容替換

  • 字符串分割

下面我們就分別通過對這幾個功能的實例實現對上面這些函數和方法以及參數和屬性的使用做下說明和具體的解釋。

1. 匹配測試

匹配測試,意思是通過特定的正則表達式對一個指定的字符串進行匹配來判斷該字符串是否符合這個正則表達式所要求的格式。常見的使用場景舉例:

  • 查看某個字符串(通常來自用戶輸入)是否是一個郵箱或電話號碼

  • 用戶注冊時填寫的用戶名和密碼是否符合指定需求

使用的函數或方法

通過re模塊執(zhí)行匹配測試時可以使用的函數或方法是:

  • re模塊級別的match()和fullmatch()函數

  • 正則表達式對象的match()和fullmatch()方法

實例1

前面提到過,match()函數或方法只是匹配字符串的開始位置,而fullmatch匹配的是整個字符串;fullmatch()函數或方法就相當于給match()函數或方法的pattern或string參數加上行首邊界元字符'^'和行尾邊界元字符'$',下面來看個例子:

import re# 定義一個函數來對匹配結果進行展示 def display_match_obj(match_obj):
    if match_obj is None:
        print('Regex Match Fail!')    else:
        print('Regex Match Success!', match_obj)if __name__ == '__main__':
    p = re.compile(r'[a-z]+')
    display_match_obj(p.match('hello'))
    display_match_obj(p.match('hello123'))
    display_match_obj(p.fullmatch('hello'))
    display_match_obj(p.fullmatch('hello123'))
    
    display_match_obj(p.match('123hello'))
    display_match_obj(p.match('123hello', 3))

輸出結果:

Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Fail!Regex Match Fail!Regex Match Success! <_sre.SRE_Match object; span=(3, 8), match='hello'>

分析:

  • '[a-z]+'能與'hello'和'hello123'的開頭部分匹配

  • '[a-z]+'能與'hello'完全匹配

  • '[a-z]+'不能與'hello123'完全匹配

  • '[a-z]+'不能與'123hello'的開頭部分匹配

  • '[a-z]+'能與'123hello'的切片'123hello'[3:]的開頭部分匹配

實例2

上面使用的是正則表達式對象的match()和fullmatch()方法,我們也可以通過re提供的模塊級別的函數來實現:

if __name__ == '__main__':
    p = re.compile(r'[a-z]+')
    display_match_obj(re.match(p, 'hello'))
    display_match_obj(re.match(p, 'hello123'))
    display_match_obj(re.fullmatch(p, 'hello'))
    display_match_obj(re.fullmatch(p, 'hello123'))
    
    display_match_obj(re.match(p, '123hello'))
    display_match_obj(re.match(p, '123hello'[3:]))  # 唯一不同的是這里

輸出結果跟上面是一樣的

re模塊的match()和fullmatch()函數不支持pos和endpos參數,所以只能通過字符串切片先對字符串進行切割。

實例3

再來看個對比:

if __name__ == '__main__':
    display_match_obj(re.match(r'[a-z]+', 'hello123'))
    display_match_obj(re.match(r'[a-z]+$', 'hello123'))
    display_match_obj(re.match(r'^[a-z]+$', 'hello123'))

輸出結果:

Regex Match Success! <_sre.SRE_Match object; span=(0, 5), match='hello'>Regex Match Fail!Regex Match Fail!

分析:

  • re.match()和re.fullmatch()中的pattern參數可以是正則表達式對象,也可以是Python字符串

  • re.match()函數中的正則表達式參數加上邊界元字符'^'和'$'就相當于re.fullmatch()了

  • 當匹配過程是從字符串的第一個字符開始匹配時re.match(r'^[a-z]+$', 'hello123') 與 re.match(r'[a-z]+$', 'hello123')效果是一樣的,因為re.match()本來就是從字符串開頭開始匹配的;但是,如果匹配過程不是從字符串的第一個字符開始匹配時,它們是有區(qū)別的,具體請看下面這個例子。

實例4
if __name__ == '__main__':
    p1 = re.compile(r'[a-z]+$')
    p2 = re.compile(r'^[a-z]+$')
    display_match_obj(p1.match('123hello', 3))
    display_match_obj(p2.match('123hello', 3))

輸出結果:

Regex Match Success! <_sre.SRE_Match object; span=(3, 8), match='hello'>Regex Match Fail!

這個很好理解,因為元字符'^'匹配的是表示字符串開始位置的特殊字符,而不是字符串內容的第一個字符。match()匹配的是字符串內容的第一個字符,因此即使是在MULTILINE模式,re.match()也將只匹配字符串的開始位置,而不是該字符串每一行的行首。

2. 內容查找

內容查找,意思是通過特定的正則表達式對一個指定的字符串的內容進行掃描來判斷該字符串中是否包含與這個正則表達式相匹配的內容。

使用的函數或方法

通過re模塊執(zhí)行匹配測試時可以使用的函數或方法是:

  • re模塊級別的search()、findall()和 finditer()函數

  • 正則表達式對象的search()、findall()和finditer()方法

實例1:search() vs match()

這個例子中,我們來看下search()函數的使用,以及它與match()函數的對比。
Python提供了兩個不同的基于正則表達式的簡單操作:

  • re.match(): 該函數僅是在字符串的開始位置進行匹配檢測

  • re.search(): 該函數會在字符串的任意位置進行匹配檢測

import re

string = 'abcdef'print(re.match(r'c', string))
print(re.search(r'c', string))

輸出結果:

None<_sre.SRE_Match object; span=(2, 3), match='c'>

這個比較簡單,不做過多解析。

我們應該能夠想到,當正則表達式以'^'開頭時,search()也會從字符串的開頭進行匹配:

import re

string = 'abcdef'print(re.match(r'c', string))
print(re.search(r'^c', string))
print(re.search(r'^a', string))

輸出結果:

NoneNone<_sre.SRE_Match object; span=(0, 1), match='a'>

這里再重復一下上面提到過的內容,就是元字符'^'與match()的從字符串開始位置開始匹配并不是完全等價的。因為元字符'^'匹配的是表示字符串開始位置的特殊字符,而不是字符串內容的第一個字符。match()匹配的是字符串內容的第一個字符,因此即使是在MULTILINE模式,re.match()也將只匹配字符串的開始位置,而不是該字符串每一行的行首;相關search('^...')卻可以匹配每一行的行首。

下面來看個例子:

string = '''
A
B
X
'''print(re.match(r'X', string, re.MULTILINE))
print(re.search(r'^X', string, re.MULTILINE))

輸出結果:

None<_sre.SRE_Match object; span=(5, 6), match='X'>
實例2:findall()與finditer()

findall()與finditer()也是用來查找一個字符串中與正則表達式相匹配的內容,但是從名字上就能看出來,findall()與finditer()會講這個字符串中所有與正則表達式匹配的內容都找出來,而search()僅僅是找到第一個匹配的內容。另外findall()返回的是所有匹配到的字串所組成的列表,而finditer()返回的是一個迭代器對象,該迭代器對象會將每一次匹配到的結果都作為一個匹配對象返回。下面來看一個例子:

嘗試找出一個字符串中的所有副詞(英語的副詞通常都是以字母‘ly’結尾):

import re

text = "He was carefully disguised but captured quickly by police."print(re.findall(r"\w+ly", text))

輸出結果:

['carefully', 'quickly']

如果我們想要獲取關于所有匹配內容的更多信息,而不僅僅是文本信息的話,就可以使用finditer()函數。finditer()可以提供與各個匹配內容相對應的匹配對象,然后我們就可以通過這個匹配對象的方法和屬性來獲取我們想要的信息。

我們來嘗試獲取一個字符串中所有副詞以及它們各自在字符串中的切片位置:

import re

text = "He was carefully disguised but captured quickly by police."for m in re.finditer(r"\w+ly", text):
    print("%02d-%02d: %s" % (m.start(), m.end(), m.group()))

輸出結果:

07-16: carefully40-47: quickly

3. 內容替換

傳統的字符串操作只能替換明確指定的子串,而使用正則表達式默認是對一個字符串中所有與正則表達式相匹配的內容進行替換,也可以指定替換次數。

可使用的函數或方法
  • re模塊的sub()和subn()函數

  • 正則表達式對象的sub()和subn()方法

sub函數返回的是被替換后的字符串,如果字符串中沒有與正則表達式相匹配的內容,則返回原始字符串;subn函數除了返回被替換后的字符串,還會返回一個替換次數,它們是以元組的形式返回的。下面來看個例子:將一個字符串中的所有的'-'字符刪除

import re

text = 'pro----gram-files'print(re.sub(r'-+', '', text))
print(re.subn(r'-+', '', text))

輸出結果:

programfiles
('programfiles', 2)

說明: 被替換的內容(repl參數)可以是一個字符串,還可以是一個函數名。該函數會在每一次匹配時被調用,且該函數接收的唯一的參數是當次匹配相對應的匹配對象,通過這個函數我們可以一些邏輯更加復雜的替換。

比如,上面那個例子中,如果我們想要得到'program files'這個結果,我們就需要把多個'-'和當個'-'分別替換為 空字符串 和 一個空白字符:

def dashrepl(match_obj):
    if match_obj.group() == '-':        return ' '
    else:        return ''if __name__ == '__main__':
    text = 'pro----gram-files'
    print(re.sub(r'-+', dashrepl, text))
    print(re.subn(r'-+', dashrepl, text))

輸出結果:

program files
('program files', 2)

說明: 當被替換的內容(repl參數)是一個字符串時可以使用'\1'、'\g<NAME>'來引用正則表達式中的捕獲組所匹配到的內容,但是需要注意的是這個字符串必須帶上r前綴。

下面來看個例子:把一個函數名前面加上'py_'前綴

p = r'def\s+([A-Za-z_]\w*)\s*\((?P<param>.*)\)'repl = r'def py_\1(\g<param>)'text = 'def myfunc(*args, **kwargs):'print(re.sub(p, repl, text))

輸出結果:

def py_myfunc(*args, **kwargs):

4. 字符串分割

通過正則表達式對字符串進行分割的過程是:掃描整個字符串,查找與正則表達式匹配的內容,然后以該內容作為分割符對字符串進行分割,最終返回被分割后的子串列表。這對于將文本數據轉換為Python易于讀取和修改的結構化數據非常有用。

可以使用的函數或方法
  • re模塊的split()函數

  • 正則表達式對象的split()方法

我們可以通過maxsplit參數來限制最大切割次數。

實例1:簡單示例
print(re.split(r'\W+', 'Words, words, words.'))
print(re.split(r'\W+', 'Words, words, words.', 1))
print(re.split(r'[a-f]+', '0a3B9', flags=re.IGNORECASE))

輸出結果:

['Words', 'words', 'words', '']
['Words', 'words, words.']
['0', '3', '9']

分析:

  • 第一行代碼中,一共分割了3次(分隔符分別為:兩個', '和一個'.'),因此返回的列表中有4個元素;

  • 第二行代碼中,限制了最大分割次數為1,因此返回的列表中只有2個元素;

  • 第三行代碼中,指定分隔符為一個或多個連續(xù)的小寫字字母,但是指定的flag為忽略大小寫,因此大寫字母也可以作為分隔符使用;那么從小寫字母'a'和大寫字母'B'分別進行切割,所以返回的列表中有3個元素。

實例2:捕獲組與匹配空字符串的正則表達式
  • 如果用于作為分割符的正則表達式包含捕獲組,那么該捕獲組所匹配的內容也會作為一個結果元素被返回;

  • 從Python 3.5開始,如果作為分隔符的正則表達式可以匹配一個空字符串,將會引發(fā)一個warning;

  • 從Python 3.5開始,如果作為分隔符的正則表達式只能匹配一個空字符串,那么它將會被拒絕使用并拋出異常。

http://www.cnblogs.com/yyds/p/6953348.html