re — 正則表示式操作

原始碼: Lib/re/


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

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

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

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

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

參見

第三方 regex 模組,它具有與標準庫 re 模組相容的 API,但提供額外的功能和更全面的 Unicode 支援。

正則表示式語法

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

可以將正則表示式連線起來以形成新的正則表示式;如果 AB 都是正則表示式,那麼 AB 也是一個正則表示式。通常,如果字串 pA 匹配,而另一個字串 qB 匹配,則字串 pq 將與 AB 匹配。除非 AB 包含低優先順序操作;AB 之間的邊界條件;或具有編號的組引用,否則這種情況成立。因此,可以很容易地從更簡單的原始表示式(如這裡描述的表示式)構建複雜的表示式。有關正則表示式的理論和實現的詳細資訊,請查閱 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’。

+

使生成的正則表示式匹配前一個正則表示式的一次或多次重複。例如,ab+ 將匹配 ‘a’ 後面跟著任意數量(非零)的 ‘b’;它不會只匹配 ‘a’。

?

使生成的正則表示式匹配前一個正則表示式的零次或一次重複。例如,ab? 將匹配 ‘a’ 或 ‘ab’。

*?, +?, ??

'*''+''?' 量詞都是 貪婪的;它們會盡可能多地匹配文字。有時,這種行為不是我們想要的;如果正則表示式 <.*>'<a> b <c>' 匹配,它將匹配整個字串,而不僅僅是 '<a>'。在量詞後新增 ? 會使其以 非貪婪最小 方式執行匹配;儘可能匹配的字元。使用正則表示式 <.*?> 將僅匹配 '<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}

指定前一個正則表示式必須精確匹配 m 次;少於此次數的匹配將導致整個正則表示式不匹配。例如,a{6} 將匹配正好六個 'a' 字元,但不會匹配五個。

{m,n}

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

{m,n}?

使生成的正則表示式匹配前一個正則表示式的 mn 次重複,並嘗試匹配儘可能的重複次數。這是前一個量詞的非貪婪版本。例如,在 6 個字元的字串 'aaaaaa' 上,a{3,5} 將匹配 5 個 'a' 字元,而 a{3,5}? 將僅匹配 3 個字元。

{m,n}+

使生成的正則表示式匹配前一個正則表示式的 mn 次重複,並嘗試匹配儘可能多的重複次數,而不建立任何回溯點。這是上述量詞的佔有版本。例如,在 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-]),它將匹配文字 '-'

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

  • 諸如 \w\S(如下定義)之類的字元類也可以在集合內部接受,儘管它們匹配的字元取決於使用的標誌

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

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

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

3.7 版本更改: 如果字元集包含未來語義會發生變化的構造,則會引發 FutureWarning 警告。

|

A|B,其中 *A* 和 *B* 可以是任意正則表示式,建立一個正則表示式,它將匹配 *A* 或 *B*。 可以用 '|' 符號分隔任意數量的正則表示式。這也可以在組內使用(見下文)。當掃描目標字串時,會從左到右嘗試由 '|' 分隔的正則表示式。當一個模式完全匹配時,該分支被接受。這意味著一旦 *A* 匹配,即使 *B* 會產生更長的整體匹配,也不會進一步測試 *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 (詳細)

(這些標誌在 模組內容 中描述。)如果您希望將標誌作為正則表示式的一部分包含在內,而不是將 *flag* 引數傳遞給 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)

如果具有給定 idname 的組存在,則會嘗試與 yes-pattern 匹配,如果不存在,則與 no-pattern 匹配。no-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 字母,則生成的正則表示式將匹配第二個字元。例如,\$ 匹配字元 '$'

\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 標誌,則單詞邊界由當前區域設定確定。

注意

請注意,\B 不匹配空字串,這與其他程式語言(如 Perl)中的正則表示式實現不同。保留此行為是為了相容性原因。

\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

僅在字串的結尾匹配。

正則表示式解析器也接受 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 (str) 模式啟用了 Unicode 匹配,並且能夠處理不同的區域設定和語言。

在 3.6 版本中更改: LOCALE 只能與位元組模式一起使用,並且與 ASCII 不相容。

在 3.7 版本中更改: 帶有 LOCALE 標誌的已編譯正則表示式物件不再依賴於編譯時的區域設定。只有匹配時的區域設定才會影響匹配結果。

re.M
re.MULTILINE

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

對應於內聯標誌 (?m)

re.NOFLAG

表示沒有應用任何標誌,該值為 0。此標誌可以用作函式關鍵字引數的預設值,或者用作將與其他標誌進行有條件 OR 運算的基本值。用作預設值的示例

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 值來修改表示式的行為。 值可以是任何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 值來修改表示式的行為。 值可以是任何flags變數,使用按位或(| 運算子)組合。

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

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

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

如果要在 string 中的任何位置查詢匹配項,請改用 search()(另請參閱 search() 與 match())。

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

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

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

可以透過指定 flags 值來修改表示式的行為。 值可以是任何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 值來修改表示式的行為。 值可以是任何flags變數,使用按位或(| 運算子)組合。

在 3.1 版本中更改:添加了可選的 flags 引數。

在 3.7 版本中更改:添加了對在可能匹配空字串的模式上進行拆分的支援。

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

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

以字串或元組列表的形式返回 patternstring 中的所有非重疊匹配項。 從左到右掃描 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 值來修改表示式的行為。 值可以是任何flags變數,使用按位或(| 運算子)組合。

在 3.7 版本中更改:非空匹配現在可以在之前的空匹配之後立即開始。

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

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

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

在 3.7 版本中更改:非空匹配現在可以在之前的空匹配之後立即開始。

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

返回透過將 string 中最左側的非重疊 pattern 出現次數替換為替換項 repl 而獲得的字串。 如果找不到模式,則返回 string,而不進行任何更改。 repl 可以是字串或函式;如果它是字串,則會處理其中的任何反斜槓轉義。 也就是說,\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-'

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

可以透過指定 flags 值來修改表示式的行為。 值可以是任何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 值來修改表示式的行為。 值可以是任何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')

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

>>> 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 值。這是正則表示式引擎開始查詢匹配的字串中的索引。

Match.endpos

傳遞給 search()match() 方法的 正則表示式物件endpos 值。這是正則表示式引擎將不會超出範圍的字串中的索引。

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” 代表 ace,“k” 代表 king,“q” 代表 queen,“j” 代表 jack,“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']]

:? 模式匹配姓氏後的冒號,以便它不出現在結果列表中。使用 4maxsplit,我們可以將門牌號從街道名稱中分離出來

>>> [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() 和函式來“mung”文字,或隨機化句子中每個單詞的所有字元的順序,但第一個和最後一個字元除外

>>> 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. Mastering Regular Expressions. 3rd ed., O’Reilly Media, 2009. 本書的第三版不再涵蓋 Python,但第一版詳細介紹了編寫優秀的正則表示式模式。