1. 程式人生 > >No.12 Python 中的正則表示式使用

No.12 Python 中的正則表示式使用

1. Python中如何使用正則表示式

Python中使用正則表示式的步驟如下:

  • 使用import re匯入正則表示式模組
  • 使用re.compile()建立一個物件
  • 使用Regex物件的search()方法,傳入一個字串,然後返回一個Match物件
  • 呼叫Match物件的group()方法,返回文字中匹配該正則表示式的字串

示例如下,查詢學生姓名中姓Zhang的同學姓名

import re

namelist = "Li Ming;Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;"
nameRegex = re.compile(r"Zhang\s\w+")
match =
nameRegex.search(namelist) print(match.group())

結果如下:

Zhang San

上面的程式碼中,有幾個地方需要解釋一下:

  • re.compile(r"Zhang San\s\w+")在正則表示式的前面加了一個r,標識該字元為原始字串。因為,在Python中,轉義字元前面需要加\來標記,如果你需要在字串中打出\,那麼你需要使用\\,或者在字串的前面加入一個r來標記

    r"Zhang San\s\w+""Zhang San\\s\\w+"是等價的

  • search()函式匹配文字中第一個符合該字串的結果並返回一個Match物件,Match

    物件的group()函式將返回被查詢到的實際文字。所以在上述結果中,我們僅得到Zhang San這個結果。如果你的正則表示式中含有分組(後續會講到),你可以使用group(1), group(2)來查詢正則表示式中第一個,第二個分組的匹配結果。

2. 正則表示式的更多模式
1. 使用括號分組

假設,某地區的電話號碼的表示形式為123-456-7890的形式,且前三位為區號,後七位標識電話號,要求將從文字中同時獲取區號,電話號和整體的電話號碼。

程式碼和結果如下:

text = "My phone number is 455-789-1234"
pnRegex = re.compile(r"(\d\d\d)-(\d\d\d-\d\d\d\d)"
) match = pnRegex.search(text) print(match.group()) print(match.group(1)) print(match.group(2)) print(match.groups())

結果如下:

455-789-1234
455
789-1234
(‘455’, ‘789-1234’)

group()預設傳入引數為0,即返回整個匹配的文字。如果想獲取全體分組的結果,使用groups()函式,該函式返回一個包含所有分組匹配結果的元組。

2. 使用管道匹配多個分組

字元|是正則表示式中的管道,用來匹配許多表達式中的一個。如果想匹配姓名列表中,姓Zhang的和姓Li的同學的姓名,可以使用管道|來連線多個正則表示式。

 namelist1 = "Li Ming;Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;"
 namelist2 = "Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;Li Ming;"
 nameRegex = re.compile(r"Zhang\s\w+|Li\s\w+")
 match1 = nameRegex.search(namelist1)
 print(match1.group())
 match2 = nameRegex.search(namelist2)
 print(match2.group())

結果如下:

Li Ming
Zhang San

3. 使用問號實現可選匹配

例如在之前的電話匹配中,我們希望即便有人省略區號,依然可以檢測出電話號碼。使用?來實現部分匹配的模式是可選的

text1 = "My phone number is 455-789-1234"
text2 = "My phone number is 789-1234"
pnRegex = re.compile(r"(\d\d\d-)?(\d\d\d-\d\d\d\d)")
match1 = pnRegex.search(text1)
print(match1.group())
match2 = pnRegex.search(text2)
print(match2.group())

結果如下:

455-789-1234
789-1234

4. 使用花括號匹配特定次數

假設現在我們有一串字串:

* *** ********** ** *** ****** ** ***** * ******* ***** **** ***** * *** * **

如果我們想匹配一下幾種情況:

  • 恰好三個* 連在一起的,如***
  • 少於等於三個* 連在一起的,如**,*
  • 連在一起的*個數大於等於四,但是小於等於五
  • 大於等於六個*連在一起的,如******

程式碼如下:

text = "* *** ********** ** **** ****** ** ***** * ******* ***** **** ***** * *** * **"
sRegex1 = re.compile(r"(\*){3}")
sRegex2 = re.compile(r"(\*){,3}")
sRegex3 = re.compile(r"(\*){4,5}")
sRegex4 = re.compile(r"(\*){6,}")
match1 = sRegex1.search(text)
match2 = sRegex2.search(text)
match3 = sRegex3.search(text)
match4 = sRegex4.search(text)
print(match1.group())
print(match2.group())
print(match3.group())
print(match4.group())

結果如下所示:

***
*
*****
**********

r"(\*){3}"中,(\*)表示匹配*型字元的分組。因為 *在正則表示式中表示匹配一個或多個,所以需要使用\進行轉義,表示字元 *

花括號{n,m},表示前面的分組重複次數大於等於n次並且小於等於m次。m和n也可省略其中一個,表示大於等於n或者小於等於m。{n}表示分組恰好重複n次。

另外,可以看到,在被匹配的文字中,長度為4的字串****排在長度為5的字串***** 前面,但是程式碼查詢到的結果是*****,這是因為預設情況下正則表示式是貪婪地,花括號的貪婪版本會盡可能的匹配更長的字串。使用字元?可以宣告正則表示式為非貪心形式

text = "* *** ********** ** *** ****** **** ***** * ******* ***** **** ***** * *** * **"
sRegex1 = re.compile(r"(\*){4,5}")
match1 = sRegex1.search(text)
sRegex2 = re.compile(r"(\*){4,5}?")
match2 = sRegex2.search(text)
print(match1.group())
print(match2.group())

結果如下:

*****
****
5. findall()方法

re模組的findall()方法返回被匹配文字中的所有匹配到的結果。

之前提到的search() 僅返回文字中第一個匹配到的結果,方法返回一個Match物件,並呼叫Match物件的group()函式獲取匹配結果

findall()匹配文字中所有匹配的結果,並且返回一個所有結果的列表。如果正則表示式中有分組,那麼findall()將返回分組的列表

比如之前的電話號的正則表示式:

text = "My phone number is 455-789-1234,Lily's phone number is 110-101-1230 and Lucy's phone number is 789-456-1245"
pnRegex = re.compile(r"(\d\d\d)-(\d\d\d-\d\d\d\d)")
reslist = pnRegex.findall(text)
print(reslist)

結果如下:

[(‘455’, ‘789-1234’), (‘110’, ‘101-1230’), (‘789’, ‘456-1245’)]

6. sub()函式和compile()函式

前面我們使用了re.compile() 來構造特定正則表示式的Regex物件,compile()函式具體的簽名如下:

def compile(pattern, flags=0)

所以我們可以在compile()追加第二個引數,實現特定功能。

例如:檢索文字中所有的Alice詞語,不區分大小寫,程式碼如下

text = "Alice is aLice,aliCe,and ALIcE.But it's not Bob."
regex = re.compile("Alice", re.IGNORECASE)
res = regex.findall(text)
print(res)

結果如下所示:

[‘Alice’, ‘aLice’, ‘aliCe’, ‘ALIcE’]

flags`的其他引數如下:

引數 縮寫 用途
re.IGNORECASE re.I Perform case-insensitive matching.
re.LOCALE re.L Make \w, \W, \b, \B, dependent on the current locale.
re.MULTILINE re.M “^” matches the beginning of lines (after a newline) as well as the string. “$” matches the end of lines (before a newline) as well as the end of the string.
re.DOTALL re.D “.” matches any character at all, including the newline.
re.VERBOSE re.X Ignore whitespace and comments for nicer looking RE’s.
re.UNICODE re.U For compatibility only. Ignored for string patterns (it is the default), and forbidden for bytes patterns.

sub()函式可以用於替換正則表示式查詢到的字串。例如,我們將之前字串中所有的Alice的姓名隱藏:

text = "Alice is aLice,aliCe,ALIcE.But it's not Bob."
regex = re.compile(r"(A)(\w+)", re.IGNORECASE)
print(regex.sub(r'\1****',text))

結果如下所示:

A**** is a****,a****,A****.But it’s not Bob.

sub()函式第一個引數是要替換為的字串,第二個引數是匹配的正則表示式。另外在sub()函式的第一個引數中,可以使用\1,\2 …來表示替換分組1,2…中的文字

正則表示式中的常用字元表,網上資源很多,這裡不再放出來。

P.S. 文章不足之處還望指正
參考書籍:《Python程式設計快速上手—讓繁瑣工作自動化》