re --- 正規表示式 (regular expression) 操作¶
原始碼:Lib/re/
此模組提供類似於 Perl 中正規表示式的匹配操作。
被搜尋的模式 (pattern) 與字串可以是 Unicode 字串 (str),也可以是 8-bit 字串 (bytes)。然而,Unicode 字串和 8-bit 字串不能混用:也就是,你不能用 byte 模式匹配 Unicode 字串,反之亦然;同樣地,替換時,用來替換的字串必須與模式和搜尋字串是相同的型別 (type)。
正規表示式使用反斜線字元 ('\') 表示特別的形式,或是使用特殊字元而不觸發它們的特殊意義。這與 Python 在字串文本 (literal) 中,為了相同目的使用同一個字元的做法相衝突;舉例來說,為了匹配一個反斜線字面值,可能需要寫 '\\\\' 當作模式字串,因為正規表示式必須是 \\,而且每個反斜線在一個普通的 Python 字串文本中必須表示為 \\。另外,請注意在 Python 的字串文本中使用反斜線的任何無效跳脫序列目前會產生一個 SyntaxWarning,而在未來這會變成一個 SyntaxError。儘管它對正規表示式是一個有效的跳脫序列,這種行為也會發生。
解決方法是對正規表示式模式使用 Python 的原始字串符號;反斜線在一個以 'r' 為前綴的字串文本中不會被用任何特別的方式處理。所以 r"\n" 是一個兩個字元的字串,包含 '\' 和 'n',同時 "\n" 是一個單個字元的字串,包含一個換行符號。通常模式在 Python 程式中會使用這個原始字串符號表示。
請務必注意到大部分的正規表示式操作是可以在模組層級的函式和 compiled regular expressions 中的方法使用的。這些函式是個捷徑且讓你不需要先編譯一個正規表示式物件,但是會缺少一些微調參數。
也參考
第三方的 regex 模組,有著和標準函式庫 re 模組相容的 API,但是提供額外的功能以及更完整的 Unicode 支援。
正規表示式語法¶
一個正規表示式(或 RE)會指定一組匹配它的字串;這個模組的函式讓你可以檢查一個特定的字串是否匹配給定的正規表示式(或是給定的正規表示式是否匹配特定的字串,說到底是一樣的)。
正規表示式可以被連接 (concatenated) 成新的正規表示式;如果 A 和 B 都是正規表示式,則 AB 也是一個正規表示式。一般來說,如果一個字串 p 與 A 匹配,並且另一個字串 q 與 B 匹配,則字串 pq 會匹配 AB。或:這在以下情況下不成立:A*或 *B 包含低優先順序的運算子;或 A 和 B 之間的邊界條件 (boundary conditions);或是有編號群組 (numbered group) 的參照。因此,複雜的正規表示式可以輕鬆地從此處提到的簡單原始正規表示式建立。關於正規表示式的理論和實作,請參考 Friedl 書 [Frie09],或任何有關建構編譯器的書籍。
一個簡短的正規表示式格式解釋如下。如需更多資訊或以較平易的方式了解,請參閱 如何使用正規表示式。
正規表示式可以包含特殊和一般字元。大多數一般字元,如 'A'、'a' 或 '0' 是最簡單的正規表示式;它們只匹配它們自己。你可以連接一般字元,讓 last 匹配字串 'last'。(在本節的其餘部分,我們會用通常沒有引號的 這種特殊樣式 來書寫正規表示式,而被匹配的字串會 '在單引號中'。)
某些字元是特殊的,像 '|' 或 '('。特殊字元會代表一般字元的類別,或是會影響它們周圍正規表示式的解讀方法。
重複運算子或量詞 (quantifiers)(*, +, ?, {m,n} 等)不能直接嵌套使用。這樣避免了和非貪婪修飾符號後綴 ? 和其他實作中的修飾符號的模糊性。要在一個重複正規表示式內添加第二個重複,可以使用括號。例如:正規表示式 (?:a{6})* 會匹配任何六的倍數個 a 字元。
特殊字元包含:
.(點) 在預設模式下,這會匹配除換行符號外的任何字元。如果指定了
DOTALL旗標,這會匹配包含換行符號的任何字元。無論旗標為何,(?s:.)都會匹配任何字元。
^(插入符號) 會匹配字串的開頭,並且在
MULTILINE模式下,也會立即匹配緊接在每個換行符號之後的位置。
$匹配字串的結尾或是字串結尾的換行符號之前,並且在
MULTILINE模式下會匹配換行符號之前的位置。foo會匹配 'foo' 和 'foobar',而正規表示式foo$只會匹配 'foo'。更有趣的是,在'foo1\nfoo2\n'中搜尋foo.$,正常情況下會匹配 'foo2',但是在MULTILINE模式下會匹配 'foo1';在'foo\n'中搜尋單一個$會找到兩個(空的)匹配:一個在換行符號之前,一個在字串結尾。
*使得到的正規表示式匹配前面正規表示式的 0 或多次的重複,盡可能多次的重複。
ab*會匹配 'a'、'ab',或後面跟著任何數量個 'b' 的 'a'。
+使得到的正規表示式匹配前面正規表示式的 1 或多次的重複。
ab+會匹配後面跟著任何非零數量個 'b' 的 'a';它不會匹配 'a'。
?使得到的正規表示式匹配前面正規表示式的 0 或 1 次的重複。
ab?會匹配 'a' 或 'ab'。
*?,+?,??量詞 (quantifier)
'*'、'+'和'?'都是 greedy(貪婪的);它們會盡可能地匹配更多文字。有時候這種行為不是預期的;如果正規表示式<.*>被用來匹配'<a> b <c>',它會匹配整個字串,而不只是'<a>'。在量詞後添加?會讓他以 non-greedy(非貪婪的)或 minimal(最小的)方式進行匹配;盡可能匹配更少的字元。使用正規表示式<.*?>只會匹配到'<a>'。
*+,++,?+類似於量詞
'*'、'+'和'?',添加了'+'的量詞也會盡可能多次的匹配。然而,不同於真正的貪婪量詞,這些量詞在接續的正規表示式匹配失敗時不允許回溯 (back-tracking)。這些量詞被稱為 possessive (佔有的)量詞。例如,a*a會匹配'aaaa',因為a*會匹配全部 4 個'a',但是在遇到最後的'a'時,此正規表示式會回溯,以讓a*最終可以總共匹配 3 個'a',而第四個'a'會被最後的'a'匹配。然而,當a*+a被用來匹配'aaaa'時,a*+會匹配全部 4 個'a',但是當最終的'a'在尋找更多字元匹配失敗時,此正規表示式不能回溯並會因此匹配失敗。x*+、x++和x?+分別等同於(?>x*)、(?>x+)和(?>x?)。在 3.11 版被加入.
{m}指定應該恰好匹配 m 個前面的正規表示式;太少的匹配會造成整個正規表示式匹配失敗。例如,
a{6}會匹配恰好六個'a'字元,但不匹配五個。{m,n}使得到的正規表示式重複匹配前面的正規表示式 m 到 n 次,並嘗試盡可能多次的匹配。例如,
a{3,5}會匹配 3 到 5 個'a'字元。忽略 m 會指定下限為零,且忽略 n 會指定無限大的上限。舉例來說,a{4,}b會匹配'aaaab'或一千個'a'字元加上一個'b',但是不匹配'aaab'。不可以忽略逗號,否則量詞會與前述的形式混淆。{m,n}?使得到的正規表示式重複匹配前面的正規表示式 m 到 n 次,嘗試盡可能更少次的匹配。這是上一個量詞的非貪婪版本。例如,對六個字元的字串
'aaaaaa',a{3,5}會匹配 5 個'a'字元,而a{3,5}?只會匹配 3 個字元。{m,n}+使得到的正規表示式重複匹配前面的正規表示式 m 到 n 次,並嘗試盡可能更多次的匹配而不建立任何回溯點。這是上述量詞的佔有版本。例如,對六個字元的字串
'aaaaaa',a{3,5}+aa嘗試匹配 5 個'a'字元,然後需要再 2 個'a',會需要比可用的字元更多所以失敗,而a{3,5}aa會捕捉到 5 個'a',然後回溯成 4 個'a',接著最後兩個'a'會被模式中最後的aa匹配。x{m,n}+等同於(?>x{m,n})。在 3.11 版被加入.
\用來跳脫特殊字元(允許你匹配如
'*'、'?'等字元),或是表示一個特殊序列;特殊序列將在下方討論。如果你沒有使用原始字串來表示正規表示式,請記得 Python 也會使用反斜線作為字串文本的跳脫序列;如果 Python 的剖析器辨認不出該跳脫序列,反斜線和接續的字元將會被包含在得到的字串中。然而,如果 Python 能辨認出該序列,反斜線應該要被重複兩次。這很複雜並且難以理解,所以強烈建議,除了最簡單的正規表示式,都應該使用原始字串。
[]用來表示一個字元集合。在一個集合中:
字元可以獨立列出,例如:
[amk]會匹配'a'、'm'或'k'。
可以透過給定兩個字元並且使用
'-'分隔它們來表示一個範圍的字元,例如[a-z]會匹配任何 ASCII 小寫字母,[0-5][0-9]會匹配所有從00到59的兩位數字,而[0-9A-Fa-f]會匹配任何十六進位數字。如果-被跳脫了(如:[a\-z]) 或是放在第一或最後一個位置(如:[-a]或[a-]),它將會匹配字面的'-'。除反斜線外的特殊字元會在集合內失去它們的特殊意義。例如,
[(+*)]會匹配任何字面字元'('、'+'、'*',或')'。
反斜線可能用於跳脫集合內具有特殊意義的字元,像是
'-'、']'、'^'和'\\'本身,或是表示一個代表單一字元的特殊序列,像是\xa0或\n或一個字元類別,如\w或\S(定義如下)。請注意,\b代表單一個 "backspace" 字元,而不是在集合外代表的字元邊界,且像是\1的數值跳脫總是代表八進位數字的跳脫,而不是群組的參照。不匹配單一字元的特殊序列是不被允許的,像是\A和\Z。
不在範圍內的字元可以透過取集合的補集 (complement) <complementing> 匹配。如果集合中的第一個字元是
'^',所有不在集合內的字元都會被匹配。例如,[^5]會匹配任何除了'5'的字元,而[^^]會匹配任何除了'^'的字元。如果^不是集合中的第一個字元的話,沒有特殊意義。要在集合中匹配字面的
']',在前面加上一個反斜線,或是將它放在集合中的最前面。例如,[()[\]{}]和[]()[{}]都會匹配右方括號、左方括號、花括號和括號。
未來可能會支援 Unicode Technical Standard #18 中的嵌套集合與集合操作。這會改變語法,所以為了促進這個改變,暫時會在模糊的情況下引發
FutureWarning。包含了以字面'['開頭,或是包含字面的字元序列'--'、'&&'、'~~'和'||'的集合。為了避免警告,請用反斜線跳脫它們。
在 3.7 版的變更: 如果一個字元集合包含了其語義未來會被改變的結構,
FutureWarning會被引發。
|A|B,其中 A 和 B 可以為任何正規表示式,會創造出一個匹配 A 或是 B 的正規表示式。任何數量的正規表示式可以像這樣用'|'分隔。這也可以用在群組(見下文)中。當目標字串被掃描時,被'|'分隔的正規表示式會被從左到右嘗試。當一個模式完全匹配時,那條分支就被接受了。這代表一旦 A 匹配了,B 就不會被進一步測試,就算它會讓整個匹配更長。換句話說,'|'永遠不是貪婪的。要匹配字面的'|',使用\|,或是將它包圍在一個字元類別中,就像[|]。
(...)匹配括號中的任何正規表示式,並且表示一個群組的開始和結束;群組的內容可以在執行匹配後取得,並且在後續的字串可以使用
\number特殊序列來匹配,如下所述。要匹配字面的'('或')',用\(或\),或是將它們包圍在一個字元類別中:[(、[)]。
(?...)這是一個擴充表示法(否則一個
'?'接著一個'('是沒有意義的)。'?'後面的第一個字元決定了這個結構的意義和進一步的語法。擴充通常不會創造新的群組;(?P<name>...)是這個規則的唯一例外。以下是目前支援的擴充。(?aiLmsux)(集合
'a'、'i'、'L'、'm'、's'、'u'、'x'中的一或多個字母。)該群組會匹配空字串;這些字母會為整個正規表示式設定對應的旗標。(這些旗標在 模組內容 中說明。)如果你想將旗標當作正規表示式的一部分,而不是傳遞一個 flag 引數給
re.compile()函式的話,這會有幫助。旗標應該首先在正規表示式字串中使用。在 3.11 版的變更: 此結構只能用於正規表示式的開頭。
(?:...)一個非捕捉版本的普通括號。匹配括號內的任何正規表示式,但是被群組匹配到的子字串不能在執行匹配後被取得,或是在之後的模式中被參照。