Python 正規表示式教程

在本教程中,我們將學習正規表示式以及 Python re 中模組中定義的正規表示式操作。re 是 Python 的標準庫,它支援正規表示式的匹配操作。

Python 中的正規表示式是一組字元或序列,用於使用正式語法將字串與另一個模式匹配。你可以將正規表示式視為嵌入在 python 中的小型程式語言。

你可以使用正規表示式來定義一些規則,然後使用這些規則從你希望與模式匹配的給定字串中建立可能的字串。Python 中的正規表示式被解釋為一組指令。

match() 函式:

你可以使用 match() 函式將 RE 模式與給定字串來匹配。match() 函式也包含了標誌,標誌定義正規表示式的行為,它可以有不同的值,後續我們在本教程會繼續講到。

以下是 Python 中 match() 函式的語法:

re.match(pattern, string, flags)

它有三個引數,

  1. pattern 是要匹配的正規表示式模式
  2. string 是與正規表示式匹配的給定字串
  3. flags 用於更改正規表示式的行為,這是個可選項

如果匹配成功就返回 Match 物件,否則返回 NONE。匹配物件 Match 還有兩個主要方法,即 group(num)group()。可以使用這些方法來分別返回匹配的特定子序列和所有子序列。

使用 match 函式

我們來看下如何使用 match 函式,

import re
strTest = "Hello Python Programming"
mobj = re.match(r"hello", strTest, re.I)
print(mobj.group())

第一行如何匯入了 re 模組,

第二行是我們需要匹配的字串 Hello Python Programming

第三行是需要將該字串跟匹配模式 r"Hello"比較,並且 re.I 指定的匹配模式是忽略大小寫。最終匹配的結果賦值給 mobj

第四行將匹配的結果給列印出來,結果如下,

Hello

在此示例中,使用字首 r 來表示字串是原始字串。在原始字串中,在使用轉義序列時不需要寫雙斜槓,例如,如果你想要一個反斜槓,那麼你只需要一個\,而不用像普通字串那樣的雙反斜槓\\

match 函式與常規字串

我們來看一個不用原始字串而用常規字串來做匹配的例子,

import re
str = "\\tHello Python Programming"
mobj = re.match("\\thello", str, re.I) #no match

str = "\tHello Python Programming"
mobj = re.match("\\thello", str, re.I) #\thello is matching

search() 函式

你可以使用 search() 函式搜尋給定字串中的正規表示式匹配模式。search 有三個輸入引數,匹配模式,給定字串以及可選的匹配行為選項 flags

以下是 Python 中 search 函式的語法,

re.search(pattern, string, flags)

我們來看具體的 search() 函式使用例項,

import re
str = "Hello Python Programming"
sobj = re.search(r"programming", str, re.I)
print(sobj.group())
Programming

在此程式碼中,我們來搜尋給定字串中是否存在 programmingsearch 函式搜尋整個字串。搜尋 search 和匹配 match 之間的區別在於 match 函式只檢查字串的開頭,而 search 在整個字串中搜尋。

在字串開頭搜尋

如果你想在字串的開頭搜尋,那麼可以使用^。來看下例,

import re
str = "Hello Python Programming"
sobj = re.search(r"^programming", str, re.I)
print(sobj.group()) #no match is found

sobj = re.search(r"^hello", str, re.I)
print(sobj.group()) #matching: Hello

這裡, ^ 的意思是隻在字串的開頭進行搜尋,假如開頭不匹配的話,就返回 None,而不管字串後續中有沒有再匹配到。

在字串結尾搜尋

你也可以在給定字串的末尾搜尋,通過在模式後面加$來限定。來看這個例子,

import re
str = "Hello Python Programming"
sobj = re.search(r"programming$", str, re.I)
print(sobj.group()) #matching: Programming

sobj = re.search(r"hello$", str, re.I)
print(sobj.group()) #no match found

編譯正規表示式

Python 中的正規表示式在編譯時將轉換為模式,這些模式實際上是包含不同功能的模式物件,以執行不同的任務,包括搜尋,匹配和替換等。

編譯模式後,你可以稍後在程式中使用該模式。

使用預編譯的模式

在下面的例子中,被編譯的模式 r"\d" 意思是在字串中的第一個數字。當該模式後續跟 search 一起使用的時候,它搜尋輸入字串中的一個數字;同樣的,你也可以用這個模式跟 match 搭配來找到給定字串中的匹配。

import re
compPat = re.compile(r"(\d)")
sobj = compPat.search("Lalalala 123")
print(mobj.group())

mobj = compPat.match("234Lalalala 123456789")
print(mobj.group())
1
2

標誌位 Flags

你可以使用標誌位 Flags 來改變正規表示式的行為。在正則函式中,標誌位是可選項。你可以通過兩種不同的方式來使用標誌,即使用關鍵字 flags 併為其分配標誌值或直接寫入標誌位的值。你可以設定多個標誌位,這可以通過使用按位或運算|符來完成。

下表列出了正規表示式的一些常用標誌,

標誌位 說明
re.I 在匹配時忽略字串和模式的大小寫
re.L 匹配{\w \W \b \B}跟本地語言相關。不推薦使用
re.M $匹配行末尾,而不是字串末尾,同理^匹配行開頭而不是字串開頭
re.S .匹配任何字元,也包括新的一行
re.U 使用 Unicode 字符集
re.X 忽略各種空格以及以#開頭的註釋,這使得長匹配模式可以分行來寫,提高了可讀性

使用多個標誌位

我們用下面的程式碼來演示如何來使用多個標誌位來修改正規表示式的行為結果,它們是通過或邏輯符|分開的。

import re
s = re.search("L", "Hello")
print(s)		#Output: None, L is there but in small letter and we didn't use flags

s = re.search("L", "Hello", re.I)
print(s)		#Output: 1

s = re.search("L", "^Hello", re.I | re.M)
print(s)		#Output: 1, searching will be made from the start of line and case is ignored

檢查允許的字元

你可以檢查一個特定的字串中是否是否含有特性的字符集。

定義函式並檢查允許的字元

在下面的例子中,我們定義了一個函式,並使用預編譯模式來檢查某些字元是否在給定的字串中,

import re
def check(str):
	s = re.compile(r'[^A-Z]')
	str = s.search(str)
	return not bool(str)
print(check("HELLOPYTHON"))		# 輸出: True
print(check("hellopython"))		# 輸出: False

在此函式中,模式 r '[^A-Z]'被編譯並使用它來搜尋在呼叫名為 check 的函式中傳遞的字串。此函式實際檢查傳遞的字串是否包含大寫字母。類似地,你可以看到當你傳遞的字串中只含有小寫字母時返回 false

In this function, a pattern that is r '[ ^A-Z]' is compiled and used it to search in a string passed when this function named check is called. This function actually checks if the passed string contains letters A-Z (uppercase) or not. Similarly, it can be seen that when you pass a string in lowercase letters false is returned, this is because re.I flag value is not used which ignores case.

搜尋和替換

re 模組提供了一個 sub 函式來替換給定字串 string 中出現的所有符合 pattern 匹配模式的情況,用來替換的字串是 repl。你可以通過 count 關鍵字引數指定最大的替換次數,假如 count 沒有給定的話,那就沒有最大替換次數限制。sub 函式返回了一個新的字串。

以下是 sub 函式的語法,

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

sub 函式舉例

下面的例子中,sub 函式將整個字串用新的字串替換掉了,

import re
s = "Playing 4 hours a day"
obj = re.sub(r'^.*$',"Working",s)
print(obj)
Working

這裡,模式 r'^.*$ 中,^$意思是從開頭到結尾,.*意思是匹配字串中的任意字元,它們結合起來就是匹配從開頭到結尾的任意字元。 "Working"將來替換整個字串 s.

使用 sub 函式來刪除字串中的所有數字

下面的例子中,使用 sub 函式來刪除給定字串中的數字,你需要用\d 來匹配數字。

import re
s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r'\d',"",s)
print(obj)
Working   five   hours   a  day

類似的,你可以來刪除字串中的字母,用 \D 來匹配所有的字母.

import re
s = "768 Working 2343 789 five 234 656 hours 324 4646 a 345 day"
obj = re.sub(r'\D',"",s)
print(obj)
76823437892346563244646345

findall() 函式

The findall 函式返回與模式匹配的所有字串組成的列表。searchfindall 函式之間的區別在於 findall 查詢所有匹配項,而 search 只查詢第一個匹配項。findall 函式查詢出非重疊的匹配並將其組成列表來返回。

以下是 findall 函式的語法,

findall(pattern, string, flags)

這裡, pattern 是正則表達, string 是給定字串, flags 是前面已經介紹的標誌位。

查詢所有的非重疊的匹配

下面的例子中,findall 找出所有非重疊的匹配,

import re
str = "Working 6 hours a day. Studying 4 hours a day."
mobj = re.findall(r'[0-9]', str)
print(mobj)
['6', '4']

r'[0-9]' 的意思是匹配所有的數字,最終結果 6, 4 以列表的形式被返回賦值給 mobj

findall 查詢檔案的內容:

你還可以使用 findall 在檔案中查詢。當你使用 findall 來查詢檔案內容時,它將返回檔案中所有匹配的字串的列表,我們可以使用檔案的 read() 方法來讀取檔案中的全部內容,因此你不必來使用迴圈。下面我們具體通過例子來解釋,

import re
file = open('asd.txt', 'r')
mobj = re.findall(r'arg.', file.read())
print(mobj)
file.close()
['arg,', 'arg,', 'arg,', 'argv', 'argv', 'argv']

本例中,檔案通過只讀模式開啟,然後匹配模式 r'arg.' 用來匹配四個字元中其中前三個字元是 arg 而第四個字元任意的情況。所有的匹配字串通過列表來返回結果。

finditer() 函式

finditer 函式可用於在字串中查詢正規表示式模式並將匹配的字串以及字串的位置返回。

以下是 finditer 函式的語法:

finditer(pattern, string, flags)

遍歷所有的匹配

findallfinditer 之間唯一的區別是 finditer 返回索引以及匹配的字串。在下面的程式碼中,finditer 用於查詢匹配字串的位置,而後通過 for 迴圈來在遍歷所有的匹配(匹配字串)。

import re
str = "Working 6 hours a day. Studying 4 hours a day."
pat = r'[0-9]'
for mobj in re.finditer(pat, str):
    s = mobj.start()
    e = mobj.end()
    g = mobj.group()
    print('{} found at location [{},{}]'.format(g, s, e))
6 found at location [8,9]
4 found at location [32,33]

split() 函式:

split 函式用於拆分字串,以下是 split 函式的語法,

split(patter, string, maxsplit=0, flags=0)

這裡 maxsplit 是最多的拆分次數,假如能夠拆分的次數大於 maxsplit 那麼剩餘的字串將作為列表中的最後一個元素。maxsplit 的預設值是 0,意思是可以無限拆分。

拆分一個字串

我們可以通過 split 來拆分字串,下面的例子中,字串根據給定的模式以及最大拆分的數量來拆分。

import re
str = "Birds fly high in the sky for ever"
mobj = re.split('\s+', str, 5)
print(mobj)
['Birds', 'fly', 'high', 'in', 'the', 'sky for ever']

本例中,模式 \s 用來匹配所有的空白字元,它等效於各種空白字元的集合,包括空格,製表符,回車等,具體如[ \t\n\r\f\v]。所以你可以通過它將各個拆分開來。這裡的最大拆分次數是 5,所以結果列表中有 6 個元素,最後一個元素是最後一次拆分後剩下的所有的字串。

Basic patterns of re 的基本模式:

正規表示式可以指定與給定字串進行比較的模式。以下是正規表示式的基本模式,

模式 描述
^ 在字串開頭匹配
$ 在字串的結尾處匹配
. 匹配任意一個字元(不包括換行符)
[...] 匹配括號內的單個字元。
[^...] 匹配不在括號中的單個字元
* 給定字串中出現 0 次或更多次
+ 給定字串中出現 1 次或多次前面的
? 給定字串中出現 0 次或 1 次
{n} 匹配給定字串中出現次數為 n
{n,} 匹配給定字串中出現次數為 n 次或多次
{n,m} 匹配給定字串中的出現次數為至少 n 個,最多 m 個
`a b`
(re) 此模式用於對正規表示式進行分組,它將記住匹配的文字,也叫反身引入
(?imx) 它將暫時在 RE 上切換 i 或 m 或 x。使用括號時,只會影響括號區域。
(?-imx) 它會暫時關閉 RE 中的 i 或 m 或 x。使用括號時,只會影響括號區域。
(?: re) 此模式用於對正規表示式進行分組,但不會記住匹配的文字。
(?imx: re) 它將臨時在括號內的 RE 或 i 或 RE 中切換。
(?-imx: re) 它會在括號內暫時切換 i 或 m 或 x 中的 RE。
(?#...) 這是一個評論。
(?= re) 它用於通過使用模式指定位置。它沒有任何範圍。
(?! re) 它用於通過使用模式否定來指定位置。它沒有任何範圍。
(?> re) 此模式用於匹配獨立模式。
\w 此模式用於匹配單詞。
\W 此模式用於匹配非單詞。
\s 它將匹配空格。\ s 等於[\ t \ n \ r \ n]。
\S 它將匹配非空格。
\d \ d 等於[0-9]。它匹配字串中的數字。
\D 匹配非數字
\A 匹配字串的開頭。
\Z 匹配字串的結尾。如果有任何換行符,它將在換行符之前匹配。
\z 匹配字串的結尾。
\G \ G 用於匹配最後一場比賽結束的點。
\b 匹配在開頭或者結尾的空字元
\B 匹配不在開頭或者結尾的空字元
\n, \t, etc. \n 用於匹配換行符,\t 將匹配分隔符
\1...\9 匹配第 n 個子表示式(已分組)。
\10 \ 10 通常匹配第 n 個子表示式(已分組),如果匹配已經完成。如果匹配尚未完成\ 10 將提供字元程式碼的八進位制表示。

重複情形

下面的列表中列出了匹配當中的重複情形以及它們的具體說明。

舉例 說明
ab? 匹配 a 或者 ab
ab* 匹配 a 或者 a 後面接任意個 b,比如 ab, abb
ab+ 匹配 a 以及至少一個 b
\d{2} 匹配兩個數字
\d{2,} 匹配兩個或多個數字
\d{2,4} 匹配 2 到 4 個數字

非貪婪匹配

在正規表示式在,重複一般意義上都是貪婪的,也就是它試圖儘量多的去匹配最大的重複。限定符 *, +? 是貪婪限定符,當你使用*時,它會進行貪婪匹配,會匹配字串中儘量多的字元。比如下面的例子,

import re
mobj = re.match(r'.*', "Birds fly high in sky")
print(mobj.group())
Birds fly high in the sky

你能看出來,整個字串都被匹配了。

當你一起使用 ?.+ 時,就得到了一個非貪婪匹配模式 .+? ,它會盡量少的去匹配字串中的內容。

import re
mobj = re.match(r'.*', "Birds fly high in sky")
print(mobj.group())

結果就只有一個字元,

B

正規表示式中的特殊字元和轉義

re 模組中的特殊字元以 \為開始。比如, \A 匹配字串的開頭。在上面的列表當中,我們已經具體介紹了這些特殊字元,我們將用例子來解釋它們的具體用法。

import re
str = "Birds fly high in the sky"
# \A
mobj = re.match(r'\Ab', str, re.I) #OUTPUT: B, here \A will match at beginning only.

#\d
mobj = re.match(r'\d', "4 birds are flying") #OUTPUT: 4

#\s
mobj = re.split('\s+', "birds fly high in the sky", 1) #OUTPUT: ['Birds', 'fly']

轉義函式 escape

escape 函式用來轉義字串中的字元,ASCII 字母、數字以及下劃線_不會被轉義。下面是 escape 函式的語法,

escape(pattern)

在下面的例子中,字串 www.python.com 被傳入到 escape 函式,其中的.是一個特殊的元字元,將會被轉義成\.

print(re.escape('www.python.com'))
www\.python\.com

一般而言,特殊的元字元會通過在前面加反斜槓\來轉義。

轉義特殊字元

比如說像括號 []這樣的字元,在正規表示式中具有特殊的意義。我們先看一個例子,

import re
mobj = re.search(r'[a]', '[a]b')
print(mobj.group())
a

這裡,很明顯, []沒有被匹配到,因為它們在正規表示式中的含義是匹配該括號裡面的一個字元。假如你想要匹配 []的話,你需要在它們前面加\

import re
mobj = re.search(r'\[a\]', '[a]b')
print(mobj.group())
[a]b

group() 方法

group 方法用來返回一個或多個被查詢到的匹配的子匹配字串,它的引用方法如下,

group(index)

If you have a single argument in group function, the result will be a single string but when you have more than one arguments, then the result will be a tuple (containing one item per argument).

When there is no argument, by default argument will be zero and it will return the entire match.

When the argument groupN is zero, the return value will be entire matching string.

When you specify the group number or argument as a negative value or a value larger than the number of groups in pattern then IndexError exception will occur.

Consider the code below in which there is no argument in group function which is equivalent to group(0).

import re
str = "Working 6 hours a day"
mobj = re.match(r'^.*', str)
print(mobj.group())
Working 6 hours a day

Here group() is used and you have the entire matched string.

Picking parts of matching texts

In the following example, group function is used with arguments to pick up matching groups:

import re
a = re.compile('(p(q)r)s')
b = a.match('pqrs')
print(b.group(0))
print(b.group(1))
print(b.group(2))
pqrs
pqr
q

Here group(0) returns the entire match. group(1) will return the first match which is pqr and group(2) will return the second match which is q.

Named groups:

Using named groups you can create a capturing group. This group can be referred by the name then. Consider the example below:

import re
mobj = re.search(r'Hi (?P<name>\w+)', 'Hi Roger')
print(mobj.group('name'))
Roger

Non-capturing groups:

Non-capturing group can be created using ?:. Non-capturing group is used when you do not want the content of the group.

import re
mobj = re.match("(?:[pqr])+", "pqr")
print(mobj.groups())
()