使用者:Xyy23330121/Python/正則表達式


正則表達式是處理、分割字符串的重要工具。它可以以一定的模式對字符串進行「匹配」,從而可以按模式篩選字符串或子字符串,並對篩選結果進行操作。

Python 專屬的正則表達式語法

編輯

以下語法中,一部分為 Python 的正則表達式專屬。在其它的正則表達式實現中使用可能會出錯。

基本匹配

編輯

正則表達式中,大多數字符都只匹配其自身。比如:正則表達式中 t 會唯一的匹配字符 t,而正則表達式 e 會唯一的匹配字符 e等等。

正則表達式滿足相加性質。即當「正則表達式A」匹配「字符串a」,而「正則表達式B」匹配「字符串b」時。拼接 A 和 B 得到的「正則表達式A+B」,一定匹配拼接得到的「字符串a+b」。

比如,正則表達式 test 會唯一的匹配字符串 test

元字符

編輯

對於正則表達式而言,上面的「基本匹配」是不足以滿足「按模式匹配」的需要的。為了描述模式,正則表達式設置了以下的元字符:

. ^ $ * + ? { } [ ] \ | ( )

這些元字符在正則表達式中表示特殊的意思,從而也就不能匹配字符串中相同字符。元字符之外的字符,都只匹配其自身。

以下將講解元字符的用法。

^ 字符串開頭

編輯

匹配位於字符串開頭的空字符串。比如:

  • 正則表達式 a 和字符串 aba 匹配,會得到兩個子字符串 a
  • 而正則表達式 ^a 和字符串 aba 匹配,只會匹配到位於字符串開頭的子字符串。

$ 字符串末尾

編輯

匹配字符串末尾的空字符串,如果字符串末尾是換行符,也可以匹配該換行符前的空字符串。

\ 轉義字符

編輯

通過 \,我們可以在正則表達式轉義出匹配元字符的表達式。比如:

  • \[ 唯一地匹配字符 [
  • \\ 唯一地匹配字符 \

創建正則表達式時,是通過字符串來創建的。如果要創建正則表達式 \\,需要使用字符串 "\\\\"r"\\"。為了簡便和易於閱讀,在創建正則表達式時,請務必使用「原始字符串」的方式。

字符串的轉義

編輯

Python 字符串支持的、以下轉義序列也被用作正則表達式的轉義。

轉義字符 含義
\a 響鈴(BEL)
\b 退格(BS)
\f 換頁(FF)
\n 換行(LF)
\r 回車(CR)
\t 橫向制表符(TAB)
\v 縱向制表符(VT)
\ooo 八進制數ooo所表示的字符
\xhh 二位十六進制數hh所表示的字符
\N{name} Unicode 資料庫中,名為name的字符,
name可以為別名。有些別名不被支持。
\uxxxx 四位十六進制數xxxx所表示的字符
\Uxxxxxxxx 八位十六進制數xxxxxxxx表示的字符

特別的,以下幾種方式與字符串中的使用有區別:

  • 對於 \ooo,只有以下兩種方式會被視為此轉義:
    • 首位數字是 0,比如 \01
    • 使用了完整的三位數字,比如 \123
  • 對於 \b,只有在「字符類」中,才認為是退格符。
  • 對於 \uxxxx\Uxxxxxxxx\N{name},僅在 Unicode 字符串模式才會被識別。

其它轉義

編輯
轉義 解釋
\A 只匹配字符串開頭
\b 只匹配單詞開頭或結尾
單詞開頭和結尾被定義為:
  • \w\W 之間的空字符串
  • 或者 \w 和字符串開頭或結尾之間的空字符串
\B 非 \b 的空字符串
\Z 只匹配字符串結尾

關於 \w\W,參見下面 ASCII 模式和 Unicode 模式 章節。

[...] 字符類

編輯

[] 括住的部分為字符類。字符類會匹配任何在類中的字符,比如:[aA1] 能匹配三個字符串:aA1

\以外的元字符在字符類中都不起作用。比如:[$] 會唯一的匹配字符 $

如果要添加 ] 到字符類中,則必須要使用轉義方法 \]

除上述規則以外,為簡便起見,字符類有以下的特殊的表示方式:

字符類:「-」

編輯

- 可以在字符類中表示「範圍」,比如:

  • [a-c] 能匹配 abc
  • [a-zA-Z0-9] 能匹配任何單個英文字母字符。

需要注意的是,如果把 - 放在首尾,則不會表示「範圍」,而是表示單獨的 - 字符。比如:

  • [-a] 能匹配 -a
  • [-a-c] 能匹配 -abc

字符類:「^」

編輯

若字符類中第一個字符為 ^,則認為是對字符類取反,比如:

  • [^a] 能匹配任何不是 a 的字符。
  • [^a-z] 能匹配任何不是小寫英文字母的字符。

需要注意的是,如果把 ^ 放在中間,則沒有此效果,比如:

  • [ ^] 能匹配空格字符和 ^

. 任意字符

編輯

. 可以匹配除了換行符以外的任意字符。

重複匹配

編輯

通過以下的方式,可以設定匹配次數。

* 匹配任意次

編輯

正則表達式 ab* 會匹配字符串 aababb 等。

+ 匹配 >=1 次

編輯

正則表達式 ab+ 會匹配字符串 ababb 等。但不會匹配 a

? 匹配 0 或 1 次

編輯

正則表達式 ab? 會匹配字符串 a 和字符串 ab

{m} 匹配 m 次
編輯

比如:正則表達式 a{3} 會匹配字符串 aaa

使用 a{m} 時,m 必須是正整數。

{m,n} 匹配 m ~ n 次
編輯

m 和 n 必須是正整數。

比如:正則表達式 a{2,3} 會匹配字符串 aa 和字符串 aaa

特別的,使用 {,n} 會匹配 0 ~ n 次。使用 {m,} 會匹配 m ~ 任意次。使用 {,} 等同於使用 *

重複匹配算法

編輯

在進行重複匹配時,默認會先儘可能多地進行匹配。在正則表達式 ^[a-z]*reabrer進行匹配時,會進行以下步驟:

  • [a-z]* 匹配 abrer
    • r 到字符串末尾,無法匹配,回退
  • [a-z]* 匹配 abre
    • r 匹配 r
    • e 到字符串末尾,無法匹配,回退
  • [a-z]* 匹配 abr
    • r 無法匹配 e ,回退
  • [a-z]* 匹配 ab
    • r 匹配 r
    • e 匹配 e
    • 正則表達式結束,匹配完畢,匹配到子字符串 abre

可以看出 [a-z]* 會先嘗試匹配 5 個字符、再嘗試匹配 4 個字符,從匹配字符數從多到少進行嘗試。這種嘗試的模式被稱為「貪婪模式」。

? 非貪婪模式

編輯

在代表重複匹配的結構後添加 ? ,則會改為匹配字符數從少到多進行嘗試。

比如,在正則表達式 ^[a-z]*?reabrer進行匹配時,會進行以下步驟:

  • [a-z]*? 匹配 a
    • r 無法匹配 b ,回退
  • [a-z]*? 匹配 ab
    • r 匹配 r
    • e 匹配 e
    • 正則表達式結束,匹配完畢,匹配到子字符串 abre

+ 占有模式

編輯

在代表重複匹配的結構後添加 + ,則會在貪婪模式的基礎上,不允許回退。這也被稱為「占有模式」。

比如,在正則表達式 ^a?[a-z]*+reabrer進行匹配時,會進行以下步驟:

  • a? 匹配 a
    • [a-z]*+ 匹配 brer 並「占有」
      • r 無法匹配字符串末尾,回退
        不能回退並修改[a-z]*+的匹配結果
        回退並修改在[a-z]*+之前的a?
  • a? 匹配空字符串
    • [a-z]*+ 匹配 abrer 並「占有」
      • r 無法匹配字符串末尾,回退
        不能回退並修改[a-z]*+的匹配結果
  • 正則表達式結束,匹配完畢,沒有匹配到字符串。

「占有模式」和「非貪婪模式」不能同時設置。

正則表達式 a|b 會匹配字符串 ab

特別的,如果左邊的「分支」匹配成功了,就不會嘗試對右邊的分支進行匹配。比如:

[a-z]*|[0-9]*114 和字符串進行匹配時,會先匹配 [a-z]*114,匹配失敗後再嘗試匹配 [0-9]*114。如果 [a-z]*114 匹配成功,就不會嘗試 [0-9]*114 了。舉個例子:

  • [a-z]*|[0-9]*1141140114 匹配:
    • 嘗試匹配 [a-z]*114
      • [a-z]* 匹配到空字符串
        • 114 匹配到 114
          匹配成功,匹配到 114

在匹配過程中,根本不會嘗試匹配 [0-9]*114。即便 [0-9]*114 能匹配完整的 1140114

(...) 組合

編輯

我們可以把一系列正則表達式作成組合,並對組合進行操作。比如:

  • 正則表達式 (ab)? 會匹配字符串 ab 和字符串 abab
  • 正則表達式 (ab)|c 會匹配字符串 ab 和字符串 c
匹配方式字符
字符 匹配方式
i 忽略大小寫
m 允許多行匹配
s . 會匹配任意字符
(包括換行符)
x 帶注釋的正則表達式
a ASCII 模式
u Unicode 模式
L 按本地語言區域模式

(?imsxauL) 設置匹配方式

編輯

以上的匹配方式並不絕對。如果在正則表達式中,出現了形如 (?a) 的組,則會按照該組的內容設置匹配方式。

這種類型的組不會匹配任何內容,它只匹配空字符串。

右表列出了該組中,可用的字符。這些字符可以進行組合,以同時設置多個匹配方式。

其中,ASCII 模式、 Unicode 模式和「按本地語言區域」模式互相矛盾,因此不能相互組合。在未設置這些模式時,默認使用 Unicode 模式。「按本地語言區域」的模式不可靠,不建議使用。

ASCII 模式和 Unicode 模式

編輯

ASCII 模式和 Unicode 模式設置以下轉義得到的字符類的行為:

轉義 ASCII 模式 Unicode 模式
\d [0-9] 任何屬於 Unicode 字符類別 ND 的字符。
\D [^0-9] 任何非 \d 的字符。
\s [ \t\n\r\f\v] 任何 Unicode 空白字符
\S [^ \t\n\r\f\v] 任何非 Unicode 空白字符
\w [a-zA-Z0-9_] 任何 str.isalnum() 方法返回 True 的字符
以及下劃線
\W [^a-zA-Z0-9_] 任何 str.isalnum() 方法返回 False 的字符
不包含下環線

多行匹配

編輯

多行匹配模式會影響 ^$ 的行為。

在設置了多行匹配模式後:

  • ^ 會匹配位於換行符後的空字符串。
  • $ 會匹配位於換行符前的空字符串。

帶注釋的正則表達式

編輯

此時,允許創建帶有注釋的、美觀的正則表達式。以下兩種方式得到的結果是相同的:

pattern = r"(0b[01]*)|(0o[0-7]*)|([0-9]*)|(0x[0-9a-fA-F]*)"
r = re.compile(pattern) #编译正则表达式
pattern = r"""(?x) #匹配数字:
  (0b[01]*)          #二进制数字
| (0o[0-7]*)         #八进制数字
| ([0-9]*)           #十进制数字
| (0x[0-9a-fA-F]*)   #十六进制数字
"""
r = re.compile(pattern) #编译正则表达式

此時,正則表達式會忽略字符串中的空格,以及多行字符串中、自動加入的換行符。

如果要匹配空格,則使用 \ 。如果要匹配換行符,則使用 \n

(?imsxauL-imsx:...) 設置局部匹配方式

編輯

僅對其中的正則表達式啟用或棄用某匹配方式。比如:

  • (?i)ab 會匹配:abAbaBAB
  • (?i)a(?-i:b) 會匹配:abAb。棄用了對 b 的「忽略大小寫模式」。
  • a(?i:b) 會匹配:aBAB。僅對 b 啟用「忽略大小寫模式」。

特別的,不允許簡單地棄用「ASCII 模式」「Unicode 模式」和「按本地語言區域模式」。

(?>...) 占有模式

編輯

該組會對組中的表達式啟用「占有模式」,比如:

  • x*+ 等同於 (?>x*)

(?:...) 非捕獲模式

編輯

此方法會影響 Python 在進行匹配時、保存的結果。比如:

  • (xyz) 在匹配時,會在結果中保存匹配到的 xyz
  • (?:xy(z)) 在匹配時,會匹配到相同的內容,但結果中僅保存 z

(?P<name>...) 帶命名的組

編輯

通過這種方式,可以給組進行命名。re.Match 實例的多個方法,以及 re.Pattern 實例的 sub 方法會引用命名組匹配到的內容。

被命名的組可以用以下方式,在正則表達式中引用。

(?P=name)

編輯

僅匹配第一次匹配到 (?P<name>...) 時、匹配到的內容。

import re
p = re.compile(r'''(?x)
<(?P<ele_name>[a-zA-Z]+).*?> #匹配<html>
.*                           #匹配中间
</(?P=ele_name)>             #匹配</html>
''')
s = '<p>Paragraph</pi>'
s1 = '<p>Paragraph</p>'

print(p.match(s).string[matchobj.start():matchobj.end()])  #输出空字符串,没有得到匹配。
print(p.match(s1).string[matchobj.start():matchobj.end()]) #输出:<p>Paragraph</p>

類似 (?P=name) ,但此方式也可以用於引用一般的組。

僅匹配第一次匹配到第 num 個組時,匹配到的內容。

(?#...) 注釋

編輯

作為注釋使用,僅匹配空字符串。

(?=...) 前視斷言

編輯

僅匹配空字符串。

其中的正則表達式和當前位置右側的內容相匹配,則匹配空字符串成功。否則匹配失敗。

比如:正則表達式 Isaac(?= Newton) 將會匹配 Isaac Newton 中的 Isaac,但不會匹配 Isaac Brown 中的 Isaac

這種方式被稱為前視斷言 (lookahead assertion)。

(?!...) 否定型前視斷言

編輯

僅匹配空字符串。

其中的正則表達式和當前位置右側的內容不匹配,則匹配空字符串成功。否則匹配失敗。

比如:正則表達式 Isaac(?! Newton) 將會匹配 Isaac Brown 中的 Isaac,但不會匹配 Isaac Newton 中的 Isaac

這種方式被稱為否定型前視斷言 (negative lookahead assertion)。

(?<=...) 肯定型後視斷言

編輯

僅匹配空字符串。

其中的正則表達式與當前位置左側的內容相匹配,則匹配空字符串成功。否則匹配失敗。

這種方式被稱為肯定型後視斷言 (positive lookbehind assertion)。

(?<!…) 否定型後視斷言

編輯

僅匹配空字符串。

其中的正則表達式和當前位置左側的內容不匹配,則匹配空字符串成功。否則匹配失敗。

這種方式被稱為否定型後視斷言 (negative lookbehind assertion)。

(?(num/name)yes-pattern|no-pattern) 條件匹配

編輯

在匹配時,之前已有的、\num(?P=name) 所示的組、匹配的並非空字符串,則按照 yes-pattern 進行匹配。否則,按照 no-pattern 進行匹配。

Python 使用正則表達式

編輯
旗標列表
旗標 匹配方式
re.I
re.IGNORECASE
忽略大小寫
re.M
re.MULTILINE
允許多行匹配
re.S
re.DOTALL
. 會匹配任意字符
(包括換行符)
re.X
re.VERBOSE
帶注釋的正則表達式
re.A
re.ASCII
ASCII 模式
re.U
re.UNICODE
Unicode 模式
re.L
re.LOCALE
按本地語言區域模式
re.NOFLAG 表示空旗標。
特別的:re.NOFLAG 就是整數 0
re.DEBUG 顯示有關編譯表達式的調試信息。

旗標

編輯

旗標和上述 設置匹配方式 章節的效果是相同的。右表列出了可用的旗標。

在使用旗標時,可以用「按位或」運算符 | 組合多個旗標,並把結果傳入下面函數中的 flags 參數中。

特別的:re.NOFLAG 和任何其它旗標進行組合,都會返回其它旗標。

re.DEBUG 會在使用正則表達式時,返回編譯的結果。

re.compile(pattern, flags=0)

編輯

編譯正則表達式。返回一個 re.Pattern 實例。re 模塊是通過該類型的實例方法來進行匹配操作的。

re.Pattern 的屬性
屬性 解釋
flags 旗標。是 compile 函數獲取的旗標以及正則表達式中用 (?imsxauL) 設置的匹配方式的組合。
groups 捕獲到的模式串中組的數量。
groupindex 映射由 (?P<name>) 定義的、有命名的組,其命名:索引位置組成的的字典。
如果沒有有命名的組,那字典就是空的。
pattern 編譯對象的原始樣式字符串。

re.Pattern 實例方法

編輯

簡單匹配

編輯
search(string[, pos[, endpos]])
編輯

string[pos: endpos] 中,查找匹配正則表達式的第一個位置,並返回對應的 re.Match 實例。如果沒有匹配的位置,則返回 None。

match(string[, pos[, endpos]])
編輯

類似 search 方法,但是從 string[pos: endpos] 的開頭開始匹配:不在 string[pos: endpos] 最開頭的、匹配的位置都不會被匹配到。

fullmatch(string[, pos[, endpos]])
編輯

如果 string[pos: endpos] 整個字符串都與正則表達式匹配,則返回相應的 re.Match 實例。

finditer(string[, pos[, endpos]])
編輯

string[pos: endpos] 中,查找匹配正則表達式的、所有非重複的位置。

每個位置,都會生成一個 re.Match 實例。該函數會返回生成的 re.Match 實例組成的迭代器。

findall(string[, pos[, endpos]])
編輯

類似 finditer 方法。但是輸出不同。

如果正則表達式中不包含一般的組,則會輸出匹配的子字符串組成的列表。而如果正則表達式中包含組,則只輸出組中的內容。比如:

import re
p = re.compile(r"\b[tT]\w+\b")
s1 = "This is a test string."
print(p.findall(s1)) #输出:['This', 'test']

q = re.compile(r"\b(\w+)=([^ ]+)")
s2 = '<img src="./image.png" width=100 height=100 />'
print(q.findall(s2)) #输出:[('src', '"./image.png"'), ('width', '100'), ('height', '100')]

分割

編輯
split(string, maxsplit=0)
編輯

string 中所有匹配的子字符串為分割符,返回分割後的、子字符串的列表。

最多進行 maxsplit 次分割。

特別的:

  • 如果 string 的開頭被匹配,則返回的列表會以空字符串開頭。
  • 如果正則表達式中包含一般的組(而非(?:...)的組),則會在分割中保留組內匹配的內容。
import re
p = re.compile("(a)(b)")
q = re.compile("(?:a)(?:b)")

s = "ab"

print(p.split(s)) #输出:['c', 'a', 'b', 'c']
print(q.split(s)) #输出:['c', 'c']

可以看出,使用一般的組的 p 在進行分割時,輸出的結果中包含了組中的內容。而使用非捕獲模式的 q 則沒有進行這樣的操作。

替換

編輯
sub(repl, string, count=0)
編輯

repl 所示的模式替換 string 中、匹配的內容。最多替換 count 次。repl 可以是字符串或函數。

如果 repl 是字符串,則直接進行替換,比如:

import re
p = re.compile(r"\b(\w+)=([^ ]+)")
s = '<img src="./image.png" width=100 height=100 />'
r = 'REPLACE'

print(p.sub(r, s))  #输出:<img REPLACE REPLACE REPLACE />

特別的, repl 中包含的、字符串轉義序列的原始序列都會被轉義。並且一些特殊的轉義也會被執行:

特殊轉義
轉義 注釋
\g<num> 正則表達式中,第 num 個組匹配到的內容
\num 正則表達式中,第 num 個組匹配到的內容
由於和 \ooo 衝突,num 不能超過 100,且
開頭不能為 0。
\g<name> 正則表達式中,名為 name 的組匹配到的內容
import re
p = re.compile(r"\b(?P<arg>\w+)=([^ ]+)")
s = '<img src="./image.png" />'
r = r'\g<1>\1\g<arg>'

print(p.sub(r, s))  #输出:<img srcsrcsrc />

如果 repl 是函數,則該函數應可以傳入每次匹配時,對應的 re.Match 實例,並返回替換的結果。

import re
p = re.compile(r"\b(\w+)=([^ ]+)")
s = '<img src="./image.png" width=100 height=100 />'
def r(matchobj):
    print(matchobj.string[matchobj.start():matchobj.end()])
    return "REPLACE"

print(p.sub(r, s))

輸出為:

src="./image.png"
width=100
height=100
<img REPLACE REPLACE REPLACE />
subn(repl, string, count=0)
編輯

類似 sub 方法,但返回一個元組: (替换后字符串, 替换次数)

單次使用

編輯

如果只匹配一次,也可以直接使用 re 模塊的以下函數:

函數 等同於
re.search(pattern, string, flags=0) re.compile(pattern, flags).search(string)
re.match(pattern, string, flags=0) re.compile(pattern, flags).match(string)
re.fullmatch(pattern, string, flags=0) re.compile(pattern, flags).fullmatch(string)
re.split(pattern, string, maxsplit=0, flags=0) re.compile(pattern, flags).split(string, maxsplit)
re.findall(pattern, string, flags=0) re.compile(pattern, flags).findall(string)
re.finditer(pattern, string, flags=0) re.compile(pattern, flags).finditer(string)
re.sub(pattern, repl, string, count=0, flags=0) re.compile(pattern, flags).sub(repl, string, count)
re.subn(pattern, repl, string, count=0, flags=0) re.compile(pattern, flags).subn(repl, string, count)

re.Match 的實例方法與屬性

編輯

替換字符串

編輯
expand(template)
編輯

替換字符串 template 的內容,使其被轉義。並返回替換後的字符串。

除字符串的轉義之外,\1\g<1> 等也會被轉義。

匹配到的組

編輯
group([group1, ...])
編輯

返回指定的組所匹配的內容。

其參數可以為正整數,代表組的索引(以 1 開頭)。也可以是字符串,代表組的名稱。

如果只有一個參數,則會返回字符串。如果有多個參數,則會返回字符串組成的元組。

特別的:

  • 參數 0 對應的是匹配的完整子字符串。
  • 不輸入任何參數時,等同於 self.group(0)
import re
p = re.compile(r"\b(?P<attr>\w+)=(?P<value>[^ ]+)")
s = '<img src="./image.png" width=100 height=100 />'
m = p.search(s)

print(m.group())               #输出:src="./image.png"
print(m.group(0))              #输出:src="./image.png"
print(m.group(1))              #输出:src
print(m.group(1,2))            #输出:('src', '"./image.png"')
print(m.group("attr","value")) #输出:('src', '"./image.png"')
__getitem__(g)
編輯

使用 self[g] 來調用此方法。

等同於 self.group(g)

groups(default=None)
編輯

返回包含所有匹配子組的字符串。對於總共有 n 個組的情況,等同於 self.group(1,2,...,n)

形如 (pattern)* 的組可能一次都不會被匹配。此時,該組對應的位置會輸出 default 的內容。

groupdict(default=None)
編輯

類似 groups 方法,但是返回的是 组的名称:匹配到的内容 的字典。

lastindex
編輯

該屬性保存最後一個匹配的整數索引值。如果沒有匹配的組,該屬性為 None。

嵌套中的組不會被計數。比如,對於字符串 'ab',表達式 (a)b, ((a)(b)), 和 ((ab)) 將得到 lastindex == 1 , 而 (a)(b) 會得到 lastindex == 2 。

lastgroup
編輯

最後一個匹配的命名組名字。如果沒有匹配的組,該屬性為 None。

索引

編輯
start([group])
編輯

返回 group 所示的組,在原始字符串中起始位置的索引。

如果不輸入 groupgroup 為 0,則返回匹配開始的位置。

end([group])
編輯

返回 group 所示的組,在原始字符串中結束位置的索引。

如果不輸入 groupgroup 為 0,則返回匹配結束的位置。

span([group])
編輯

返回 (start([group]), end([group])) 元組。

匹配參數

編輯

生成 re.Match 需要用到 使用 re.compile(pattern).match(string, pos, endpos) 或 re.compile(pattern).search(string, pos, endpos) 等方法。以下屬性表示其中的參數或對象。

生成此 re.Match 實例所使用的參數 pos。

endpos
編輯

生成此 re.Match 實例所使用的參數 endpos。

生成此 re.Match 實例所使用的 re.Pattern 實例。

string
編輯

生成此 re.Match 實例所使用的參數 string。即被匹配的字符串。

其它操作

編輯

re.escape(pattern)

編輯

生成一個能匹配 pattern 字符串的、正則表達式字符串。

re.purge()

編輯

清除正則表達式的緩存。

注意事項

編輯

由於「多次匹配」的匹配機制,一些正則表達式可能會導致匹配時間隨字符串長度指數增長,比如以下是導致 Cloudflare 在 2019 年 7 月發生全球宕機事故的正則表達式的一部分:

  • .*(?:.*=.*)

如果用這個正則表達式來匹配形如 title=User:Xyy23330121 的內容,它會:

  • .* 匹配到 title=User:Xyy23330121
    • .* 匹配到空字符串
      • = 到字符串尾,無法匹配,回退
    • 空字符串無法回退,繼續向前回退。
  • .* 匹配到 title=User:Xyy2333012
    • .* 匹配到 1
      • = 到字符串尾,無法匹配,回退
    • .* 匹配到空字符串
      • = 匹配到 1,無法匹配,回退
    • 空字符串無法回退,繼續向前回退。
  • .* 匹配到 title=User:Xyy233301
  • ...

從結論而言,它會嘗試匹配總共 1 + 2 + ... + 16 = 136 次才會正確匹配所有內容。而如果等號後面的內容更多,情況只會更糟。

請在設置正則表達式時儘可能慎重。

參考文獻

編輯