re 模組
July 5, 2022在 Python 中,使用 re
模組來支援規則表示式。
split/findall/sub
例如,若想切割字串,可以使用 re.split
函式:
>>> import re
>>> re.split(r'\d', 'Justin1Monica2Irene')
['Justin', 'Monica', 'Irene']
>>> re.split(r',', 'Justin,Monica,Irene')
['Justin', 'Monica', 'Irene']
>>> re.split(r'Orz', 'JustinOrzMonicaOrzIrene')
['Justin', 'Monica', 'Irene']
>>> re.split(r'\t', 'Justin\tMonica\tIrene')
['Justin', 'Monica', 'Irene']
re
模組的 split
函式,第一個參數要以字串來指定規則表示式,實際上若使用 Python 的字串表示時,因為 \
等在Python字串中被作為轉義(Escape)字元,因此要撰寫規則表示式時,例如 \d
,就必須撰寫為 '\\d'
,這樣當然很麻煩。
幸而 Python 中可以在字串前加上 r
,表示這是個原始字串(Raw string),不要對 \
做任何轉義動作,因此在撰寫規則表示式時,建議使用原始字串。
如果想找出字串中全部符合表示式的文字,可以使用 re.findall
函式,結果會以 list
傳回,若沒有符合就是傳回空 list
。
例如來實際看看貪婪量詞(Greedy quantifier)與逐步量詞(Reluctant quantifier)的差別:
>>> re.findall(r'.*foo', 'xfooxxxxxxfoo')
['xfooxxxxxxfoo']
>>> re.findall(r'.*?foo', 'xfooxxxxxxfoo')
['xfoo', 'xxxxxxfoo']
例如分別比對出來的名稱最後必須有或沒有 'Lin'
:
>>> re.findall(r'\w+\s(?=Lin)', 'Justin Lin, Monica Huang, Irene Lin')
['Justin ', 'Irene ']
>>> re.findall(r'\w+\s(?!Lin)', 'Justin Lin, Monica Huang, Irene Lin')
['Monica ']
如果要取代符合的子字串,可以使用 re.sub
函式。例如,將所有單引號都換成雙引號:
>>> re.sub("'", '"', "your right brain has nothing 'left' and your left brain has nothing 'right'")
'your right brain has nothing "left" and your left brain has nothing "right"'
如果規則表示式中有分組設定,在使用 sub
時,可以使用 \num
來捕捉被分組匹配的文字,num
表示第幾個分組。
例如,以下示範如何將使用者郵件位址從 .com 取代為 .cc:
>>> re.sub(r'(^[a-zA-Z]+\d*)@([a-z]+?.)com', r'\1@\2cc', 'caterpillar@openhome.com')
'caterpillar@openhome.cc'
整個規則表示式匹配了 'caterpillar@openhome.com'
,第一個分組捕捉到 'caterpillar'
,第二個分組捕捉到 'openhome.'
,\1
與 \2
就分別代表這兩個部份。
如果使用了 (?P<name>…)
為分組命名,在呼叫 sub
函式時,必須使用 \g<name>
來參考。例如:
>>> re.sub(r'(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com', r'\g<user>@\g<preCom>cc', 'caterpillar@openhome.com')
'caterpillar@openhome.cc'
subn/escape
re.sub
會將全部符合的文字都進行取代,傳回取代後的字串,re.subn
與 re.sub
功能相同,然而傳回一個 Tuple,第一個元素是取代後的字串,第二個是符合的文字數量:
>>> re.sub(r'\d\d', 'Orz', '12dd1232')
'OrzddOrzOrz'
>>> re.subn(r'\d\d', 'Orz', '12dd1232')
('OrzddOrzOrz', 3)
在〈字元、字元類〉談過,詮譯字元在規則表示式中有特殊意義,例如 $
^
*
(
)
+
=
{
}
[
]
|
\
:
.
?
等,若要比對這些字元,則必須加上轉義(Escape)符號,即使 Python 有原始字串表示,自己處理這些事也還是麻煩,這時可以使用 re.escape
來代勞:
>>> re.escape(r'python.exe')
'python\\.exe'
search/match
如果想在搜尋某字串,看看是否有子字串符合規則表示式,可以使用 re.search
,若無匹配傳回 None
,若有匹配,會傳回 re.Match
物件,例如:
>>> import re
>>> m = re.search(r'\d{3}', "Kaohsiung 803, Road 12")
>>> m.group()
'803'
>>> m.start()
10
>>> m.end()
13
可以使用 Match
物件的 group
方法來取得符合整個規則表示式的子字串,使用 start
來取得子字串的起始索引,end
來取得結尾索引。
與 search
類似的是 match
函式,search
會在整個字串中,找尋第一個符合的子字串,而 match
只會在字串開頭看看接下來的字串是否符合,若有符合也是傳回 Match
物件,若無傳回 None
:
>>> m = re.match(r'\d{3}', "Kaohsiung 803, Road 12")
>>> m == None
True
>>> m = re.match(r'\d{3}', "803, Road 12")
>>> m.group()
'803'
若規則表示式有分組,Match
物件的 group
方法可以傳入數字,取得指定的分組,group
可以接受多個數字作為引數:
>>> m = re.match(r'(\w+) (\w+)', 'Isaac Newton, physicist')
>>> m.group(0)
'Isaac Newton'
>>> m.group(1)
'Isaac'
>>> m.group(2)
'Newton'
>>> m.group(1, 2)
('Isaac', 'Newton')
如果分組時有命名,group
也可以接受名稱:
>>> m = re.match(r'(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com', 'caterpillar@openhome.com')
>>> m.group('user')
'caterpillar'
>>> m.group('preCom')
'openhome.'
>>> m['user']
'caterpillar'
>>> m[1]
'caterpillar'
>>> m.groups()
('caterpillar', 'openhome.')
>>> m.groupdict()
{'user': 'caterpillar', 'preCom': 'openhome.'}
由於 Match
實作了 __getitem__
方法,如上所示,你也可以使用 []
來取得分組結果;Match
物件的 groups
方法,則用來取得全部的分組結果,groupdict
用來取得 dict
,包含了分組命名與結果。
更多 Match
物件的方法或特性,可以參考〈Match Objects〉。
如果要求整個字串符合規則表示式,可以使用 re.fullmatch
(Python 3.4),例如:
>>> m = re.fullmatch(r'(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com', 'caterpillar@openhome.com is valid')
>>> m == None
True
>>> re.match(r'(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com', 'caterpillar@openhome.com is valid')
<re.Match object; span=(0, 24), match='caterpillar@openhome.com'>