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程式設計快速上手—讓繁瑣工作自動化》