re — 正則表示式操作

原始碼: Lib/re/


本模組提供了類似於 Perl 中的正則表示式匹配操作。

要搜尋的模式和字串可以是 Unicode 字串 (str) 以及 8 位字串 (bytes)。但是,Unicode 字串和 8 位字串不能混合使用:也就是說,不能用位元組模式匹配 Unicode 字串,反之亦然;同樣,在請求替換時,替換字串的型別必須與模式和搜尋字串的型別相同。

正則表示式使用反斜槓字元 ('\') 來表示特殊形式或允許在不呼叫其特殊含義的情況下使用特殊字元。這與 Python 在字串字面量中出於相同目的使用相同字元發生衝突;例如,要匹配一個字面反斜槓,可能需要將 '\\\\' 寫為模式字串,因為正則表示式必須是 \\,並且每個反斜槓必須在普通的 Python 字串字面量中表示為 \\。此外,請注意,Python 在字串字面量中使用的任何無效轉義序列現在都會生成 SyntaxWarning,將來這將成為 SyntaxError。即使它是正則表示式的有效轉義序列,也會發生這種行為。

解決方案是在正則表示式模式中使用 Python 的原始字串表示法;在以 'r' 為字首的字串字面量中,反斜槓不會以任何特殊方式處理。因此 r"\n" 是一個包含 '\''n' 的兩個字元字串,而 "\n" 是一個包含換行符的一個字元字串。通常,模式將在 Python 程式碼中使用這種原始字串表示法表示。

重要的是要注意,大多數正則表示式操作都作為模組級函式和已編譯正則表示式的方法可用。這些函式是快捷方式,不需要先編譯正則表示式物件,但缺少一些微調引數。

參見

第三方 regex 模組的 API 與標準庫 re 模組相容,但提供了額外的功能和更徹底的 Unicode 支援。

正則表示式語法

正則表示式(或 RE)指定了一組與之匹配的字串;此模組中的函式允許檢查特定字串是否與給定的正則表示式匹配(或者給定的正則表示式是否與特定字串匹配,這歸結為同一件事)。

正則表示式可以連線起來形成新的正則表示式;如果 *A* 和 *B* 都是正則表示式,那麼 *AB* 也是一個正則表示式。通常,如果字串 *p* 匹配 *A*,另一個字串 *q* 匹配 *B*,則字串 *pq* 將匹配 AB。除非 *A* 或 *B* 包含低優先順序操作;*A* 和 *B* 之間的邊界條件;或者具有編號組引用。因此,複雜的表示式可以很容易地由此處描述的簡單基本表示式構建。有關正則表示式的理論和實現的詳細資訊,請查閱 Friedl 書籍 [Frie09],或幾乎任何關於編譯器構造的教科書。

正則表示式格式的簡要說明如下。有關更多資訊和更溫和的介紹,請參閱正則表示式 HOWTO

正則表示式可以包含特殊字元和普通字元。大多數普通字元,例如 'A''a''0',是最簡單的正則表示式;它們只是匹配自身。您可以連線普通字元,因此 last 匹配字串 'last'。(在本節的其餘部分,我們將以 這種特殊樣式 編寫 RE,通常不帶引號,以及要匹配的字串 '用單引號括起來'。)

某些字元,例如 '|''(' 是特殊字元。特殊字元要麼代表普通字元的類別,要麼影響其周圍正則表示式的解釋方式。

重複運算子或量詞 (*, +, ?, {m,n}, 等) 不能直接巢狀。這避免了與非貪婪修飾符字尾 ? 以及其他實現中其他修飾符的歧義。要對內部重複應用第二次重複,可以使用括號。例如,表示式 (?:a{6})* 匹配任意六個 'a' 字元的倍數。

特殊字元是

.

(點。)在預設模式下,這匹配除換行符以外的任何字元。如果指定了 DOTALL 標誌,則這匹配包括換行符在內的任何字元。(?s:.) 匹配任何字元,無論標誌如何。

^

(脫字元。)匹配字串的開頭,在 MULTILINE 模式下也匹配每個換行符之後的位置。

$

匹配字串的結尾或字串結尾換行符之前,在 MULTILINE 模式下也匹配換行符之前。foo 匹配 'foo' 和 'foobar',而正則表示式 foo$ 只匹配 'foo'。更有趣的是,在 'foo1\nfoo2\n' 中搜索 foo.$ 通常匹配 'foo2',但在 MULTILINE 模式下匹配 'foo1';在 'foo\n' 中搜索單個 $ 將找到兩個(空)匹配項:一個在換行符之前,一個在字串結尾。

*

導致生成的 RE 匹配前面 RE 的 0 次或更多次重複,儘可能多的重複。ab* 將匹配 'a'、'ab' 或 'a' 後跟任意數量的 'b'。

+

導致生成的 RE 匹配前面 RE 的 1 次或更多次重複。ab+ 將匹配 'a' 後跟任意非零數量的 'b';它不會只匹配 'a'。

?

導致生成的 RE 匹配前面 RE 的 0 次或 1 次重複。ab? 將匹配 'a' 或 'ab'。

*?, +?, ??

'*''+''?' 量詞都是 *貪婪的*;它們會盡可能多地匹配文字。有時不希望這種行為;如果 RE <.*>'<a> b <c>' 匹配,它將匹配整個字串,而不僅僅是 '<a>'。在量詞後新增 ? 會使其以 *非貪婪* 或 *最小* 方式執行匹配;將匹配 *儘可能少* 的字元。使用 RE <.*?> 將只匹配 '<a>'

*+, ++, ?+

'*', '+', 和 '?' 量詞一樣,那些附加了 '+' 的量詞也匹配儘可能多的次數。然而,與真正的貪婪量詞不同,當其後面的表示式匹配失敗時,它們不允許回溯。這些被稱為 *獨佔* 量詞。例如,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}

指定必須匹配前面 RE 的正好 *m* 個副本;匹配次數較少會導致整個 RE 不匹配。例如,a{6} 將匹配正好六個 'a' 字元,但不匹配五個。

{m,n}

導致生成的 RE 匹配前面 RE 的 *m* 到 *n* 次重複,嘗試匹配儘可能多的重複。例如,a{3,5} 將匹配 3 到 5 個 'a' 字元。省略 *m* 指定下限為零,省略 *n* 指定無限上限。例如,a{4,}b 將匹配 'aaaab' 或一千個 'a' 字元後跟一個 'b',但不匹配 'aaab'。逗號不能省略,否則修飾符會與前面描述的形式混淆。

{m,n}?

導致生成的 RE 匹配前面 RE 的 *m* 到 *n* 次重複,嘗試匹配 *儘可能少* 的重複。這是前面量詞的非貪婪版本。例如,在 6 個字元的字串 'aaaaaa' 上,a{3,5} 將匹配 5 個 'a' 字元,而 a{3,5}? 將只匹配 3 個字元。

{m,n}+

導致生成的 RE 匹配前面 RE 的 *m* 到 *n* 次重複,嘗試匹配儘可能多的重複 *而不* 建立任何回溯點。這是上述量詞的獨佔版本。例如,在 6 個字元的字串 'aaaaaa' 上,a{3,5}+aa 嘗試匹配 5 個 'a' 字元,然後,需要 2 個額外的 'a',將需要比可用字元更多的字元,因此會失敗,而 a{3,5}aa 將匹配,其中 a{3,5} 捕獲 5 個,然後透過回溯捕獲 4 個 'a',然後模式中的最後 2 個 'a' 被最後的 aa 匹配。x{m,n}+ 等價於 (?>x{m,n})

在 3.11 版本中新增。

\

要麼轉義特殊字元(允許匹配 '*''?' 等字元),要麼表示特殊序列;特殊序列將在下面討論。

如果您沒有使用原始字串來表示模式,請記住 Python 也在字串字面量中將反斜槓用作轉義序列;如果 Python 的解析器無法識別轉義序列,則反斜槓和後續字元將包含在結果字串中。但是,如果 Python 識別結果序列,則反斜槓應該重複兩次。這很複雜且難以理解,因此強烈建議您將原始字串用於除最簡單表示式以外的所有表示式。

[]

用於表示一組字元。在一個集合中

  • 字元可以單獨列出,例如 [amk] 將匹配 'a''m''k'

  • 字元範圍可以透過給出兩個字元並用 '-' 分隔它們來指示,例如 [a-z] 將匹配任何小寫 ASCII 字母,[0-5][0-9] 將匹配從 0059 的所有兩位數數字,而 [0-9A-Fa-f] 將匹配任何十六進位制數字。如果 - 被轉義(例如 [a\-z])或者如果它被放置為第一個或最後一個字元(例如 [-a][a-]),它將匹配一個字面量 '-'

  • 除反斜槓以外的特殊字元在集合內部會失去其特殊含義。例如,[(+*)] 將匹配字面字元 '(''+''*'')' 中的任何一個。

  • 反斜槓要麼轉義在集合中具有特殊含義的字元,例如 '-'']''^''\\' 本身,要麼表示一個特殊序列,該序列代表單個字元,例如 \xa0\n,或者一個字元類,例如 \w\S(下面定義)。請注意,\b 表示單個“退格”字元,而不是集合外部的單詞邊界,並且數字轉義,例如 \1 始終是八進位制轉義,而不是組引用。不允許不匹配單個字元的特殊序列,例如 \A\z

  • 不在範圍內的字元可以透過 *補集* 匹配。如果集合的第一個字元是 '^',則將匹配所有 *不* 在集合中的字元。例如,[^5] 將匹配除 '5' 以外的任何字元,而 [^^] 將匹配除 '^' 以外的任何字元。^ 如果不是集合中的第一個字元,則沒有特殊含義。

  • 要在集合內部匹配字面量 ']',請在其前面加上反斜槓,或將其放在集合的開頭。例如,[()[\]{}][]()[{}] 都將匹配右括號,以及左括號、花括號和圓括號。

  • 將來可能會新增對巢狀集和集合操作的支援,例如 Unicode 技術標準 #18。這將改變語法,因此為了方便此更改,暫時在模糊的情況下將引發 FutureWarning。這包括以字面量 '[' 開頭或包含字面字元序列 '--''&&''~~''||' 的集合。要避免警告,請使用反斜槓轉義它們。

3.7 新版功能: 如果字元集包含將來語義會改變的構造,則會引發 FutureWarning

|

A|B,其中 *A* 和 *B* 可以是任意 RE,建立一個正則表示式,它將匹配 *A* 或 *B*。任意數量的 RE 可以用 '|' 以這種方式分隔。這也可以在組內部使用(見下文)。當目標字串被掃描時,用 '|' 分隔的 RE 從左到右嘗試。當一個模式完全匹配時,該分支被接受。這意味著一旦 *A* 匹配,*B* 將不再被測試,即使它會產生更長的總體匹配。換句話說,'|' 運算子從不貪婪。要匹配字面量 '|',請使用 \|,或將其括在字元類中,例如 [|]

(...)

匹配括號內的正則表示式,並指示組的開始和結束;匹配執行後可以檢索組的內容,並且稍後可以使用下面描述的 \number 特殊序列在字串中進行匹配。要匹配字面量 '('')',請使用 \(\),或將它們括在字元類中:[(][)]

(?...)

這是一種擴充套件表示法('(' 後面的 '?' 否則沒有意義)。'?' 後面的第一個字元決定了構造的含義和進一步的語法。擴充套件通常不會建立新組;(?P<name>...) 是此規則的唯一例外。以下是當前支援的擴充套件。

(?aiLmsux)

(集合 'a''i''L''m''s''u''x' 中的一個或多個字母。)該組匹配空字串;這些字母為整個正則表示式設定相應的標誌

  • re.A (僅匹配 ASCII)

  • re.I (忽略大小寫)

  • re.L (依賴於語言環境)

  • re.M (多行)

  • re.S (點匹配所有)

  • re.U (Unicode 匹配)

  • re.X (詳細)

(這些標誌在模組內容中描述。)這對於希望將標誌作為正則表示式的一部分而不是將 *flags* 引數傳遞給 re.compile() 函式時非常有用。標誌應該首先在表示式字串中使用。

在 3.11 版本中更改: 此構造只能在表示式的開頭使用。

(?:...)

非捕獲版本的普通括號。匹配括號內的正則表示式,但匹配的子字串 *不能* 在執行匹配後檢索或在模式中稍後引用。

(?aiLmsux-imsx:...)

(集合 'a''i''L''m''s''u''x' 中的零個或多個字母,可選地後跟 '-',然後是 'i''m''s''x' 中的一個或多個字母。)這些字母設定或移除表示式相應部分的標誌

  • re.A (僅匹配 ASCII)

  • re.I (忽略大小寫)

  • re.L (依賴於語言環境)

  • re.M (多行)

  • re.S (點匹配所有)

  • re.U (Unicode 匹配)

  • re.X (詳細)

(這些標誌在模組內容中描述。)

當作為內聯標誌使用時,字母 'a''L''u' 互斥,因此它們不能組合或跟在 '-' 之後。相反,當其中一個出現在內聯組中時,它會覆蓋封閉組中的匹配模式。在 Unicode 模式中,(?a:...) 切換到僅 ASCII 匹配,而 (?u:...) 切換到 Unicode 匹配(預設)。在位元組模式中,(?L:...) 切換到區域設定相關匹配,而 (?a:...) 切換到僅 ASCII 匹配(預設)。此覆蓋僅在狹窄的內聯組中有效,原始匹配模式在該組外部恢復。

在 3.6 版本加入。

在 3.7 版本中更改: 字母 'a''L''u' 也可以在組中使用。

(?>...)

嘗試匹配 ...,就好像它是一個單獨的正則表示式一樣,如果成功,則繼續匹配其後的模式的其餘部分。如果後續模式匹配失敗,堆疊只能回溯到 (?>...) *之前* 的點,因為一旦退出,該表示式(稱為 *原子組*)已經拋棄了其內部的所有堆疊點。因此,(?>.*). 將永遠不會匹配任何東西,因為首先 .* 將匹配所有可能的字元,然後,沒有留下任何可匹配的字元,最終的 . 將匹配失敗。由於原子組中沒有儲存堆疊點,並且在其之前沒有堆疊點,因此整個表示式將因此匹配失敗。

在 3.11 版本中新增。

(?P<name>...)

類似於普通括號,但組匹配的子字串可以透過符號組名 *name* 訪問。組名必須是有效的 Python 識別符號,在 bytes 模式中,它們只能包含 ASCII 範圍內的位元組。每個組名在正則表示式中只能定義一次。符號組也是編號組,就像該組沒有命名一樣。

命名組可以在三種上下文中引用。如果模式是 (?P<quote>['"]).*?(?P=quote)(即匹配用單引號或雙引號引起來的字串)

引用組“quote”的上下文

引用方式

在同一模式本身中

  • (?P=quote) (如所示)

  • \1

處理匹配物件 *m* 時

  • m.group('quote')

  • m.end('quote') (等等)

在傳遞給 re.sub() 的 *repl* 引數的字串中

  • \g<quote>

  • \g<1>

  • \1

3.12 新版功能: bytes 模式中,組 *name* 只能包含 ASCII 範圍內的位元組 (b'\x00'-b'\x7f')。

(?P=name)

對命名組的反向引用;它匹配前面名為 *name* 的組所匹配的任何文字。

(?#...)

註釋;括號中的內容被簡單忽略。

(?=...)

如果 ... 接下來匹配,但不會消耗任何字串,則匹配成功。這被稱為 *先行斷言*。例如,Isaac (?=Asimov) 將只在後面跟著 'Asimov' 時匹配 'Isaac '

(?!...)

如果 ... 接下來不匹配,則匹配成功。這是一個 *否定先行斷言*。例如,Isaac (?!Asimov) 將只在後面 *不* 跟著 'Asimov' 時匹配 'Isaac '

(?<=...)

如果字串中的當前位置前面有一個與 ... 匹配的項,並且該匹配項在當前位置結束,則匹配成功。這稱為 *肯定後行斷言*。(?<=abc)def 將在 'abcdef' 中找到匹配項,因為後行斷言將回溯 3 個字元並檢查包含的模式是否匹配。包含的模式只能匹配固定長度的字串,這意味著 abca|b 允許,但 a*a{3,4} 不允許。請注意,以肯定後行斷言開頭的模式不會在被搜尋字串的開頭匹配;您很可能希望使用 search() 函式而不是 match() 函式。

>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'

此示例查詢連字元後面的單詞

>>> m = re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'

3.5 新版功能: 增加了對固定長度組引用的支援。

(?<!...)

如果字串中的當前位置前面沒有與 ... 匹配的項,則匹配成功。這稱為 *否定後行斷言*。與肯定後行斷言類似,包含的模式只能匹配固定長度的字串。以否定後行斷言開頭的模式可能在被搜尋字串的開頭匹配。

(?(id/name)yes-pattern|no-pattern)

如果存在給定 *id* 或 *name* 的組,則嘗試匹配 yes-pattern,如果不存在,則匹配 no-patternno-pattern 是可選的,可以省略。例如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是一個糟糕的電子郵件匹配模式,它將匹配 '<user@host.com>''user@host.com',但不匹配 '<user@host.com' 也不能匹配 'user@host.com>'

在 3.12 版本中更改: 組 *id* 只能包含 ASCII 數字。在 bytes 模式中,組 *name* 只能包含 ASCII 範圍內的位元組 (b'\x00'-b'\x7f')。

特殊序列由 '\' 和下面列表中的一個字元組成。如果普通字元不是 ASCII 數字或 ASCII 字母,則生成的 RE 將匹配第二個字元。例如,\$ 匹配字元 '$'

\number

匹配相同編號的組的內容。組從 1 開始編號。例如,(.+) \1 匹配 'the the''55 55',但不匹配 'thethe'(請注意組後面的空格)。此特殊序列只能用於匹配前 99 個組中的一個。如果 *number* 的第一個數字是 0,或者 *number* 是 3 個八進位制數字長,它將不被解釋為組匹配,而是被解釋為八進位制值 *number* 的字元。在字元類的 '['']' 內部,所有數字轉義都被視為字元。

\A

僅在字串的開頭匹配。

\b

匹配空字串,但僅在單詞的開頭或結尾。單詞定義為單詞字元序列。請注意,形式上,\b 定義為 \w\W 字元之間(或反之),或 \w 與字串開頭或結尾之間的邊界。這意味著 r'\bat\b' 匹配 'at''at.''(at)''as at ay',但不匹配 'attempt''atlas'

Unicode (str) 模式中的預設單詞字元是 Unicode 字母數字和下劃線,但這可以透過使用 ASCII 標誌來更改。如果使用 LOCALE 標誌,則單詞邊界由當前語言環境決定。

備註

在字元範圍內部,\b 表示退格字元,以便與 Python 的字串字面量相容。

\B

匹配空字串,但僅當它 *不* 在單詞的開頭或結尾時。這意味著 r'at\B' 匹配 'athens''atom''attorney',但不匹配 'at''at.''at!'\B\b 相反,因此 Unicode (str) 模式中的單詞字元是 Unicode 字母數字或下劃線,儘管這可以透過使用 ASCII 標誌來更改。如果使用 LOCALE 標誌,則單詞邊界由當前語言環境決定。

3.14 新版功能: \B 現在匹配空輸入字串。

\d
對於 Unicode (str) 模式

匹配任何 Unicode 十進位制數字(即 Unicode 字元類別 [Nd] 中的任何字元)。這包括 [0-9],以及許多其他數字字元。

如果使用 ASCII 標誌,則匹配 [0-9]

對於 8 位 (bytes) 模式

匹配 ASCII 字元集中的任何十進位制數字;這等價於 [0-9]

\D

匹配任何非十進位制數字的字元。這與 \d 相反。

如果使用 ASCII 標誌,則匹配 [^0-9]

\s
對於 Unicode (str) 模式

匹配 Unicode 空白字元(如 str.isspace() 定義)。這包括 [ \t\n\r\f\v],以及許多其他字元,例如許多語言中排版規則要求的非斷行空格。

如果使用 ASCII 標誌,則匹配 [ \t\n\r\f\v]

對於 8 位 (bytes) 模式

匹配 ASCII 字元集中被視為空白字元的字元;這等價於 [ \t\n\r\f\v]

\S

匹配任何非空白字元的字元。這與 \s 相反。

如果使用 ASCII 標誌,則匹配 [^ \t\n\r\f\v]

\w
對於 Unicode (str) 模式

匹配 Unicode 單詞字元;這包括所有 Unicode 字母數字字元(如 str.isalnum() 定義),以及下劃線 (_)。

如果使用 ASCII 標誌,則匹配 [a-zA-Z0-9_]

對於 8 位 (bytes) 模式

匹配 ASCII 字元集中被認為是字母數字的字元;這等價於 [a-zA-Z0-9_]。如果使用 LOCALE 標誌,則匹配當前語言環境中的字母數字字元和下劃線。

\W

匹配任何非單詞字元。這與 \w 相反。預設情況下,匹配非下劃線 (_) 字元,對於這些字元,str.isalnum() 返回 False

如果使用 ASCII 標誌,則匹配 [^a-zA-Z0-9_]

如果使用了 LOCALE 標誌,則匹配當前語言環境中非字母數字且非下劃線的字元。

\z

僅在字串的末尾匹配。

在 3.14 版本加入。

\Z

\z 相同。為了相容舊的 Python 版本。

Python 字串字面量支援的大多數轉義序列也受正則表示式解析器接受

\a      \b      \f      \n
\N      \r      \t      \u
\U      \v      \x      \\

(請注意,\b 用於表示單詞邊界,僅在字元類內部表示“退格”)。

'\u''\U''\N' 轉義序列僅在 Unicode (str) 模式中識別。在位元組模式中,它們是錯誤。ASCII 字母的未知轉義序列保留供將來使用,並被視為錯誤。

八進位制轉義以有限形式包含。如果第一個數字是 0,或者有三個八進位制數字,則被視為八進位制轉義。否則,它是組引用。對於字串字面量,八進位制轉義的長度始終最多為三位。

3.3 新版功能: 添加了 '\u''\U' 轉義序列。

3.6 新版功能: '\' 和一個 ASCII 字母組成的未知轉義序列現在是錯誤。

3.8 新版功能: 新增了 '\N{name}' 轉義序列。與字串字面量一樣,它擴充套件為命名的 Unicode 字元(例如 '\N{EM DASH}')。

模組內容

該模組定義了幾個函式、常量和一個異常。其中一些函式是編譯後的正則表示式的全功能方法的簡化版本。大多數非平凡的應用程式總是使用編譯後的形式。

標誌

3.6 新版功能: 標誌常量現在是 RegexFlag 的例項,後者是 enum.IntFlag 的子類。

class re.RegexFlag

一個 enum.IntFlag 類,包含下面列出的正則表示式選項。

3.11 新版功能: - 新增到 __all__

re.A
re.ASCII

使 \w\W\b\B\d\D\s\S 執行僅 ASCII 匹配而不是完整的 Unicode 匹配。這僅對 Unicode (str) 模式有意義,對於位元組模式則忽略。

對應於內聯標誌 (?a)

備註

U 標誌仍然存在以實現向後相容性,但在 Python 3 中是多餘的,因為對於 str 模式,匹配預設是 Unicode,並且對於位元組模式不允許 Unicode 匹配。UNICODE 和內聯標誌 (?u) 同樣是多餘的。

re.DEBUG

顯示有關已編譯表示式的除錯資訊。

沒有相應的內聯標誌。

re.I
re.IGNORECASE

執行大小寫不敏感匹配;像 [A-Z] 這樣的表示式也將匹配小寫字母。完整的 Unicode 匹配(例如 Ü 匹配 ü)也有效,除非使用 ASCII 標誌停用非 ASCII 匹配。除非還使用了 LOCALE 標誌,否則當前語言環境不會改變此標誌的效果。

對應於內聯標誌 (?i)

請注意,當 Unicode 模式 [a-z][A-Z]IGNORECASE 標誌結合使用時,它們將匹配 52 個 ASCII 字母和 4 個額外的非 ASCII 字母:'İ' (U+0130,帶點拉丁大寫字母 I),'ı' (U+0131,無點拉丁小寫字母 i),'ſ' (U+017F,拉丁小寫長 s) 和 'K' (U+212A,開爾文符號)。如果使用 ASCII 標誌,則只匹配字母 'a' 到 'z' 和 'A' 到 'Z'。

re.L
re.LOCALE

使 \w, \W, \b, \B 和大小寫不敏感匹配依賴於當前語言環境。此標誌只能與位元組模式一起使用。

對應於內聯標誌 (?L)

警告

不建議使用此標誌;請考慮使用 Unicode 匹配。區域設定機制非常不可靠,因為它一次只處理一種“文化”,並且只適用於 8 位區域設定。Unicode 匹配預設對 Unicode (str) 模式啟用,並且能夠處理不同的區域設定和語言。

3.6 新版功能: LOCALE 只能與位元組模式一起使用,並且與 ASCII 不相容。

3.7 新版功能: 帶有 LOCALE 標誌的已編譯正則表示式物件不再在編譯時依賴於語言環境。只有匹配時的語言環境會影響匹配結果。

re.M
re.MULTILINE

指定時,模式字元 '^' 匹配字串的開頭和每一行的開頭(緊接在每個換行符之後);模式字元 '$' 匹配字串的結尾和每一行的結尾(緊接在每個換行符之前)。預設情況下,'^' 只匹配字串的開頭,'$' 只匹配字串的結尾和緊接在字串結尾處的換行符(如果有的話)之前。

對應於內聯標誌 (?m)

re.NOFLAG

表示不應用任何標誌,值為 0。此標誌可用作函式關鍵字引數的預設值,或作為將有條件地與其它標誌進行按位或操作的基值。用作預設值的示例

def myfunc(text, flag=re.NOFLAG):
    return re.match(text, flag)

在 3.11 版本中新增。

re.S
re.DOTALL

使特殊字元 '.' 匹配所有字元,包括換行符;如果沒有此標誌,'.' 將匹配 *除* 換行符之外的任何字元。

對應於內聯標誌 (?s)

re.U
re.UNICODE

在 Python 3 中,Unicode 字元預設與 str 模式匹配。因此,此標誌是多餘的,*無任何效果*,僅為向後相容性保留。

請參閱 ASCII 以限制僅匹配 ASCII 字元。

re.X
re.VERBOSE

此標誌允許您編寫更美觀、更易讀的正則表示式,方法是允許您在視覺上分離模式的邏輯部分並添加註釋。模式中的空白字元被忽略,除非在字元類中,或者前面有未轉義的反斜槓,或者在像 *?(?:(?P<...> 這樣的標記中。例如,(? :* ? 不允許。當一行包含一個不在字元類中且前面沒有未轉義的反斜槓的 # 時,從最左邊的 # 到行尾的所有字元都將被忽略。

這意味著以下兩個匹配十進位制數字的正則表示式物件在功能上是相等的

a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")

對應於內聯標誌 (?x)

函式

re.compile(pattern, flags=0)

將正則表示式模式編譯成一個正則表示式物件,該物件可以使用其 match()search() 和其他方法(如下所述)進行匹配。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

序列

prog = re.compile(pattern)
result = prog.match(string)

等價於

result = re.match(pattern, string)

但使用 re.compile() 並儲存生成的正則表示式物件以供重用,在單個程式中多次使用該表示式時會更高效。

備註

傳遞給 re.compile() 和模組級匹配函式的最新的已編譯模式都會被快取,因此一次只使用少量正則表示式的程式無需擔心編譯正則表示式。

re.search(pattern, string, flags=0)

掃描 *string* 查詢正則表示式 *pattern* 產生匹配的第一個位置,並返回相應的 Match。如果字串中沒有位置匹配模式,則返回 None;請注意,這與在字串中某一點找到零長度匹配不同。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

re.match(pattern, string, flags=0)

如果 *string* 開頭的零個或多個字元匹配正則表示式 *pattern*,則返回相應的 Match。如果字串不匹配模式,則返回 None;請注意,這與零長度匹配不同。

請注意,即使在 MULTILINE 模式下,re.match() 也只在字串的開頭匹配,而不在每一行的開頭匹配。

如果你想在 *string* 中的任何位置找到匹配項,請改用 search() (另請參閱 search() vs. match())。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

re.fullmatch(pattern, string, flags=0)

如果整個 *string* 與正則表示式 *pattern* 匹配,則返回相應的 Match。如果字串不匹配模式,則返回 None;請注意,這與零長度匹配不同。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

在 3.4 版本加入。

re.split(pattern, string, maxsplit=0, flags=0)

透過 *pattern* 的出現來拆分 *string*。如果 *pattern* 中使用了捕獲括號,則模式中所有組的文字也作為結果列表的一部分返回。如果 *maxsplit* 非零,則最多發生 *maxsplit* 次拆分,並且字串的其餘部分作為列表的最後一個元素返回。

>>> re.split(r'\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'\W+', 'Words, words, words.', maxsplit=1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

如果分隔符中有捕獲組並且它在字串的開頭匹配,則結果將以一個空字串開頭。字串的末尾也是如此

>>> re.split(r'(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']

這樣,分隔符元件總是在結果列表中以相同的相對索引找到。

相鄰的空匹配是不可能的,但空匹配可以在非空匹配之後立即發生。

>>> re.split(r'\b', 'Words, words, words.')
['', 'Words', ', ', 'words', ', ', 'words', '.']
>>> re.split(r'\W*', '...words...')
['', '', 'w', 'o', 'r', 'd', 's', '', '']
>>> re.split(r'(\W*)', '...words...')
['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

3.1 新版功能: 增加了可選的 flags 引數。

3.7 新版功能: 增加了對可匹配空字串的模式進行拆分的支援。

3.13 版本棄用: 將 *maxsplit* 和 *flags* 作為位置引數傳遞已棄用。在未來的 Python 版本中,它們將成為僅限關鍵字引數

re.findall(pattern, string, flags=0)

返回 stringpattern 的所有非重疊匹配項,作為字串列表或元組列表。 string 從左到右掃描,匹配項按發現的順序返回。 空匹配項也包含在結果中。

結果取決於模式中捕獲組的數量。 如果沒有組,則返回一個匹配整個模式的字串列表。 如果只有一個組,則返回一個匹配該組的字串列表。 如果存在多個組,則返回一個匹配這些組的字串元組列表。 非捕獲組不影響結果的形式。

>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
[('width', '20'), ('height', '10')]

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

版本 3.7 中的變化: 非空匹配項現在可以在前一個空匹配項之後立即開始。

re.finditer(pattern, string, flags=0)

返回一個 迭代器,該迭代器針對 string 中 RE pattern 的所有非重疊匹配項生成 Match 物件。 string 從左到右掃描,匹配項按發現的順序返回。 空匹配項也包含在結果中。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

版本 3.7 中的變化: 非空匹配項現在可以在前一個空匹配項之後立即開始。

re.sub(pattern, repl, string, count=0, flags=0)

返回將 stringpattern 的最左邊非重疊出現項替換為替換項 repl 後得到的字串。 如果未找到模式,則返回未更改的 stringrepl 可以是字串或函式;如果它是字串,則其中所有反斜槓轉義符都會被處理。 也就是說,\n 會轉換為單個換行符,\r 會轉換為回車符,依此類推。 ASCII 字母的未知轉義符保留供將來使用,並被視為錯誤。 其他未知轉義符(如 \&)保持不變。 反向引用(如 \6)會被替換為模式中第 6 組匹配的子字串。 例如

>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
...        r'static PyObject*\npy_\1(void)\n{',
...        'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'

如果 repl 是一個函式,則會為 pattern 的每個非重疊出現項呼叫該函式。 該函式接受單個 Match 引數,並返回替換字串。 例如

>>> def dashrepl(matchobj):
...     if matchobj.group(0) == '-': return ' '
...     else: return '-'
...
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

模式可以是字串或 Pattern

可選引數 count 是要替換的模式出現的最大次數; count 必須是非負整數。 如果省略或為零,則所有出現項都將被替換。

相鄰的空匹配是不可能的,但空匹配可以緊接在非空匹配之後發生。 因此,sub('x*', '-', 'abxd') 返回 '-a-b--d-' 而不是 '-a-b-d-'

在字串型別 repl 引數中,除了上面描述的字元轉義和反向引用之外,\g<name> 將使用由 (?P<name>...) 語法定義的名為 name 的組匹配的子字串。 \g<number> 使用相應的組號;因此 \g<2> 等效於 \2,但在諸如 \g<2>0 的替換中沒有歧義。 \20 將被解釋為對組 20 的引用,而不是對組 2 的引用後跟字面字元 '0'。 反向引用 \g<0> 替換為 RE 匹配的整個子字串。

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

3.1 新版功能: 增加了可選的 flags 引數。

版本 3.5 中的變化: 未匹配的組將替換為空字串。

版本 3.6 中的變化: pattern 中由 '\' 和 ASCII 字母組成的未知轉義現在是錯誤。

版本 3.7 中的變化: repl 中由 '\' 和 ASCII 字母組成的未知轉義現在是錯誤。 空匹配可以緊接在非空匹配之後發生。

版本 3.12 中的變化: id 只能包含 ASCII 數字。 在 bytes 替換字串中,組 name 只能包含 ASCII 範圍內的位元組 (b'\x00'-b'\x7f')。

自版本 3.13 起已棄用: countflags 作為位置引數傳遞已棄用。 在未來的 Python 版本中,它們將是 僅限關鍵字的引數

re.subn(pattern, repl, string, count=0, flags=0)

執行與 sub() 相同的操作,但返回一個元組 (new_string, number_of_subs_made)

表示式的行為可以透過指定 *flags* 值來修改。值可以是任何標誌變數,使用位或(| 運算子)組合。

re.escape(pattern)

轉義 pattern 中的特殊字元。 如果您想匹配可能包含正則表示式元字元的任意字面字串,這很有用。 例如

>>> print(re.escape('https://python.club.tw'))
https://www\.python\.org

>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print('[%s]+' % re.escape(legal_chars))
[abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+

>>> operators = ['+', '-', '*', '/', '**']
>>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))
/|\-|\+|\*\*|\*

此函式不得用於 sub()subn() 中的替換字串,只需轉義反斜槓即可。 例如

>>> digits_re = r'\d+'
>>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
>>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))
/usr/sbin/sendmail - \d+ errors, \d+ warnings

版本 3.3 中的變化: '_' 字元不再被轉義。

版本 3.7 中的變化: 只有在正則表示式中可能具有特殊含義的字元才會被轉義。 因此,'!''"''%'"'"',''/'':'';''<''=''>''@'"`" 不再被轉義。

re.purge()

清除正則表示式快取。

異常

exception re.PatternError(msg, pattern=None, pos=None)

當傳遞給此處某個函式的字串不是有效的正則表示式(例如,它可能包含不匹配的括號)或在編譯或匹配期間發生其他錯誤時引發的異常。 如果字串不包含模式的匹配項,則絕不會是錯誤。 PatternError 例項具有以下附加屬性

msg

未格式化的錯誤訊息。

pattern

正則表示式模式。

pos

編譯失敗時 pattern 中的索引(可能為 None)。

lineno

對應於 pos 的行(可能為 None)。

colno

對應於 pos 的列(可能為 None)。

版本 3.5 中的變化: 添加了附加屬性。

版本 3.13 中的變化: PatternError 最初名為 error;後者作為別名保留以實現向後相容。

正則表示式物件

class re.Pattern

re.compile() 返回的編譯後的正則表示式物件。

版本 3.9 中的變化: re.Pattern 支援 [] 以指示 Unicode (str) 或位元組模式。 參見 通用別名型別

Pattern.search(string[, pos[, endpos]])

掃描 string,查詢此正則表示式產生匹配的第一個位置,並返回相應的 Match。 如果字串中沒有位置匹配模式,則返回 None;請注意,這與在字串中某處找到零長度匹配不同。

可選的第二個引數 pos 給出了搜尋開始的字串索引;它預設為 0。 這並不完全等同於字串切片;'^' 模式字元匹配字串的實際開頭和換行符之後的位置,但不一定匹配搜尋開始的索引。

可選引數 endpos 限制了字串的搜尋範圍;它將如同字串有 endpos 個字元長,因此只會搜尋從 posendpos - 1 的字元以進行匹配。 如果 endpos 小於 pos,則不會找到匹配項;否則,如果 rx 是編譯後的正則表示式物件,rx.search(string, 0, 50) 等效於 rx.search(string[:50], 0)

>>> pattern = re.compile("d")
>>> pattern.search("dog")     # Match at index 0
<re.Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1)  # No match; search doesn't include the "d"
Pattern.match(string[, pos[, endpos]])

如果 string開頭 處有一個或多個字元匹配此正則表示式,則返回相應的 Match。 如果字串不匹配模式,則返回 None;請注意,這與零長度匹配不同。

可選的 posendpos 引數與 search() 方法的含義相同。

>>> pattern = re.compile("o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
<re.Match object; span=(1, 2), match='o'>

如果您想在 string 中的任何位置找到匹配項,請改用 search()(另請參閱 search() 與 match())。

Pattern.fullmatch(string[, pos[, endpos]])

如果整個 string 匹配此正則表示式,則返回相應的 Match。 如果字串不匹配模式,則返回 None;請注意,這與零長度匹配不同。

可選的 posendpos 引數與 search() 方法的含義相同。

>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.fullmatch("ogre")     # No match as not the full string matches.
>>> pattern.fullmatch("doggie", 1, 3)   # Matches within given limits.
<re.Match object; span=(1, 3), match='og'>

在 3.4 版本加入。

Pattern.split(string, maxsplit=0)

split() 函式相同,使用編譯後的模式。

Pattern.findall(string[, pos[, endpos]])

類似於 findall() 函式,使用編譯後的模式,但也接受可選的 posendpos 引數,這些引數像 search() 一樣限制搜尋區域。

Pattern.finditer(string[, pos[, endpos]])

類似於 finditer() 函式,使用編譯後的模式,但也接受可選的 posendpos 引數,這些引數像 search() 一樣限制搜尋區域。

Pattern.sub(repl, string, count=0)

sub() 函式相同,使用編譯後的模式。

Pattern.subn(repl, string, count=0)

subn() 函式相同,使用編譯後的模式。

Pattern.flags

正則表示式匹配標誌。 這是傳遞給 compile() 的標誌、模式中的任何 (?...) 內聯標誌以及隱式標誌(例如如果模式是 Unicode 字串,則為 UNICODE)的組合。

Pattern.groups

模式中捕獲組的數量。

Pattern.groupindex

一個字典,將 (?P<id>) 定義的任何符號組名對映到組號。 如果模式中未使用符號組,則字典為空。

Pattern.pattern

編譯模式物件的模式字串。

版本 3.7 中的變化: 增加了對 copy.copy()copy.deepcopy() 的支援。 編譯後的正則表示式物件被認為是原子性的。

匹配物件

匹配物件始終具有布林值 True。 由於 match()search() 在沒有匹配時返回 None,因此您可以使用簡單的 if 語句測試是否存在匹配

match = re.search(pattern, string)
if match:
    process(match)
class re.Match

成功 matchsearch 返回的匹配物件。

版本 3.9 中的變化: re.Match 支援 [] 以指示 Unicode (str) 或位元組匹配。 參見 通用別名型別

Match.expand(template)

返回透過對模板字串 template 進行反斜槓替換(如 sub() 方法所做的)得到的字串。 \n 等轉義符會轉換為相應的字元,數字反向引用(\1\2)和命名反向引用(\g<1>\g<name>)會替換為相應組的內容。 反向引用 \g<0> 將替換為整個匹配項。

版本 3.5 中的變化: 未匹配的組將替換為空字串。

Match.group([group1, ...])

返回匹配項的一個或多個子組。 如果只有一個引數,則結果為單個字串;如果有多個引數,則結果為元組,每個引數一個項。 沒有引數時,group1 預設為零(返回整個匹配項)。 如果 groupN 引數為零,則相應的返回值是整個匹配字串;如果在包含範圍 [1..99] 內,則是匹配相應帶括號組的字串。 如果組號為負或大於模式中定義的組數,則會引發 IndexError 異常。 如果組包含在模式中不匹配的部分中,則相應的結果為 None。 如果組包含在模式中多次匹配的部分中,則返回最後一次匹配。

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0)       # The entire match
'Isaac Newton'
>>> m.group(1)       # The first parenthesized subgroup.
'Isaac'
>>> m.group(2)       # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2)    # Multiple arguments give us a tuple.
('Isaac', 'Newton')

如果正則表示式使用 (?P<name>...) 語法,則 groupN 引數也可以是透過其組名標識組的字串。 如果字串引數未在模式中用作組名,則會引發 IndexError 異常。

一箇中等複雜的例子

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'

命名組也可以透過其索引引用

>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'

如果一個組匹配多次,則只能訪問最後一次匹配

>>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
>>> m.group(1)                        # Returns only the last match.
'c3'
Match.__getitem__(g)

這與 m.group(g) 相同。 這允許更輕鬆地訪問匹配中的單個組

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0]       # The entire match
'Isaac Newton'
>>> m[1]       # The first parenthesized subgroup.
'Isaac'
>>> m[2]       # The second parenthesized subgroup.
'Newton'

也支援命名組

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Isaac Newton")
>>> m['first_name']
'Isaac'
>>> m['last_name']
'Newton'

在 3.6 版本加入。

Match.groups(default=None)

返回一個元組,其中包含匹配的所有子組,從 1 到模式中組的數量。 default 引數用於未參與匹配的組;它預設為 None

例如:

>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')

如果我們使小數點及其之後的所有內容可選,並非所有組都可能參與匹配。 這些組將預設為 None,除非給定 default 引數

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups()      # Second group defaults to None.
('24', None)
>>> m.groups('0')   # Now, the second group defaults to '0'.
('24', '0')
Match.groupdict(default=None)

返回一個字典,其中包含匹配的所有 命名 子組,以子組名為鍵。 default 引數用於未參與匹配的組;它預設為 None。 例如

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.start([group])
Match.end([group])

返回 group 匹配的子字串的開始和結束索引; group 預設為零(表示整個匹配的子字串)。 如果 group 存在但未對匹配做出貢獻,則返回 -1。 對於匹配物件 m 和對匹配做出貢獻的組 g,組 g 匹配的子字串(等同於 m.group(g))是

m.string[m.start(g):m.end(g)]

請注意,如果 group 匹配一個空字串,則 m.start(group) 將等於 m.end(group)。 例如,在 m = re.search('b(c?)', 'cba') 之後,m.start(0) 是 1,m.end(0) 是 2,m.start(1)m.end(1) 都是 2,而 m.start(2) 會引發 IndexError 異常。

一個將從電子郵件地址中刪除 remove_this 的例子

>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'tony@tiger.net'
Match.span([group])

對於匹配 m,返回 2 元組 (m.start(group), m.end(group))。 請注意,如果 group 未對匹配做出貢獻,則為 (-1, -1)group 預設為零,即整個匹配。

Match.pos

傳遞給 search()match() 方法的 pos 值,該方法屬於 正則表示式物件。 這是 RE 引擎開始查詢匹配項的字串索引。

Match.endpos

傳遞給 search()match() 方法的 endpos 值,該方法屬於 正則表示式物件。 這是 RE 引擎不會越過的字串索引。

Match.lastindex

最後匹配捕獲組的整數索引,如果完全沒有匹配組,則為 None。 例如,表示式 (a)b((a)(b))((ab)) 應用於字串 'ab' 時,lastindex == 1,而表示式 (a)(b) 應用於相同字串時,lastindex == 2

Match.lastgroup

最後匹配捕獲組的名稱,如果該組沒有名稱,或者完全沒有匹配組,則為 None

Match.re

生成此匹配例項的 正則表示式物件match()search() 方法。

Match.string

傳遞給 match()search() 的字串。

版本 3.7 中的變化: 增加了對 copy.copy()copy.deepcopy() 的支援。 匹配物件被認為是原子性的。

正則表示式示例

檢查一對牌

在這個例子中,我們將使用以下輔助函式更優雅地顯示匹配物件

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

假設您正在編寫一個撲克程式,其中玩家的手牌表示為 5 個字元的字串,每個字元代表一張牌,“a”代表 A,“k”代表 K,“q”代表 Q,“j”代表 J,“t”代表 10,而“2”到“9”代表相應值的牌。

要檢視給定字串是否是有效手牌,可以執行以下操作

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

最後一張牌,“727ak”,包含一對,或者兩張相同值的牌。要用正則表示式匹配這個,可以使用反向引用,如下所示

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

要找出這對牌由哪張牌組成,可以使用匹配物件的 group() 方法,如下所示

>>> pair = re.compile(r".*(.).*\1")
>>> pair.match("717ak").group(1)
'7'

# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'

>>> pair.match("354aa").group(1)
'a'

模擬 scanf()

Python 目前沒有 scanf() 的等價物。 正則表示式通常比 scanf() 格式字串更強大,但也更冗長。 下表提供了一些 scanf() 格式標記和正則表示式之間或多或少的等效對映。

scanf() 標記

正則表示式

%c

.

%5c

.{5}

%d

[-+]?\d+

%e%E%f%g

[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?

%i

[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)

%o

[-+]?[0-7]+

%s

\S+

%u

\d+

%x%X

[-+]?(0[xX])?[\dA-Fa-f]+

要從如下字串中提取檔名和數字

/usr/sbin/sendmail - 0 errors, 4 warnings

您將使用類似 scanf() 的格式

%s - %d errors, %d warnings

等效的正則表示式將是

(\S+) - (\d+) errors, (\d+) warnings

search() 與 match()

Python 提供了基於正則表示式的不同原始操作

  • re.match() 僅檢查字串開頭是否有匹配項

  • re.search() 檢查字串中任何位置是否有匹配項(這是 Perl 預設執行的操作)

  • re.fullmatch() 檢查整個字串是否匹配

例如:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<re.Match object; span=(2, 3), match='c'>
>>> re.fullmatch("p.*n", "python") # Match
<re.Match object; span=(0, 6), match='python'>
>>> re.fullmatch("r.*n", "python") # No match

'^' 開頭的正則表示式可以與 search() 一起使用,以限制匹配在字串的開頭

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<re.Match object; span=(0, 1), match='a'>

請注意,在 MULTILINE 模式下,match() 僅匹配字串的開頭,而使用以 '^' 開頭的正則表示式與 search() 將匹配每一行的開頭。

>>> re.match("X", "A\nB\nX", re.MULTILINE)  # No match
>>> re.search("^X", "A\nB\nX", re.MULTILINE)  # Match
<re.Match object; span=(4, 5), match='X'>

製作電話簿

split() 將字串分割成一個由傳遞模式分隔的列表。 對於將文字資料轉換為 Python 可以輕鬆讀取和修改的資料結構,此方法非常寶貴,如下面的示例所示,該示例建立了一個電話簿。

首先,這是輸入。通常它可能來自檔案,這裡我們使用三引號字串語法

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""

條目由一個或多個換行符分隔。現在我們將字串轉換為一個列表,每個非空行都有自己的條目

>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']

最後,將每個條目分割成一個包含名字、姓氏、電話號碼和地址的列表。我們使用 split()maxsplit 引數,因為地址中包含空格,這是我們的分割模式

>>> [re.split(":? ", entry, maxsplit=3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

:? 模式匹配姓氏後的冒號,因此它不會出現在結果列表中。使用 maxsplit4,我們可以將門牌號與街道名稱分開

>>> [re.split(":? ", entry, maxsplit=4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]

文字混淆

sub() 用字串或函式的結果替換模式的所有出現。 此示例演示了使用帶有函式的 sub() 來“混淆”文字,即隨機化句子中每個單詞中除第一個和最後一個字元之外的所有字元的順序

>>> def repl(m):
...     inner_word = list(m.group(2))
...     random.shuffle(inner_word)
...     return m.group(1) + "".join(inner_word) + m.group(3)
...
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'

查詢所有副詞

findall() 匹配模式的 所有 出現項,而不僅僅是 search() 那樣只匹配第一個。 例如,如果作者想在某些文字中找到所有副詞,他們可能會按以下方式使用 findall()

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly\b", text)
['carefully', 'quickly']

查詢所有副詞及其位置

如果一個人想要比匹配文字更多的關於模式的所有匹配的資訊,那麼 finditer() 是有用的,因為它提供了 Match 物件而不是字串。 繼續前面的例子,如果一個作者想要找到一些文字中的所有副詞 及其位置,他們將按以下方式使用 finditer()

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

原始字串表示法

原始字串表示法(r"text")使正則表示式保持清晰。 如果沒有它,正則表示式中的每個反斜槓('\')都必須在其前面加上另一個反斜槓才能進行轉義。 例如,以下兩行程式碼在功能上是相同的

>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>

當要匹配字面反斜槓時,必須在正則表示式中對其進行轉義。使用原始字串表示法,這意味著 r"\\"。如果不使用原始字串表示法,則必須使用 "\\\\",使得以下幾行程式碼在功能上是相同的

>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>

編寫一個分詞器

分詞器或掃描器 分析字串以對字元組進行分類。這是編寫編譯器或直譯器的有用第一步。

文字類別透過正則表示式指定。該技術是將這些類別組合成一個主正則表示式,並迴圈進行連續匹配

from typing import NamedTuple
import re

class Token(NamedTuple):
    type: str
    value: str
    line: int
    column: int

def tokenize(code):
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
    token_specification = [
        ('NUMBER',   r'\d+(\.\d*)?'),  # Integer or decimal number
        ('ASSIGN',   r':='),           # Assignment operator
        ('END',      r';'),            # Statement terminator
        ('ID',       r'[A-Za-z]+'),    # Identifiers
        ('OP',       r'[+\-*/]'),      # Arithmetic operators
        ('NEWLINE',  r'\n'),           # Line endings
        ('SKIP',     r'[ \t]+'),       # Skip over spaces and tabs
        ('MISMATCH', r'.'),            # Any other character
    ]
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
    line_num = 1
    line_start = 0
    for mo in re.finditer(tok_regex, code):
        kind = mo.lastgroup
        value = mo.group()
        column = mo.start() - line_start
        if kind == 'NUMBER':
            value = float(value) if '.' in value else int(value)
        elif kind == 'ID' and value in keywords:
            kind = value
        elif kind == 'NEWLINE':
            line_start = mo.end()
            line_num += 1
            continue
        elif kind == 'SKIP':
            continue
        elif kind == 'MISMATCH':
            raise RuntimeError(f'{value!r} unexpected on line {line_num}')
        yield Token(kind, value, line_num, column)

statements = '''
    IF quantity THEN
        total := total + price * quantity;
        tax := price * 0.05;
    ENDIF;
'''

for token in tokenize(statements):
    print(token)

分詞器產生以下輸出

Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
[Frie09]

Friedl, Jeffrey. 精通正則表示式。第三版,O'Reilly Media,2009。該書的第三版已不再涵蓋 Python,但第一版詳細介紹瞭如何編寫優秀的正則表示式模式。