22. 正規表示式

星光映梦發表於2024-10-24

一、概述

  正規表示式(regular expression)又稱 規則表示式,是一種文字模式(pattern)。正規表示式使用一個字串來描述、匹配具有相同規格的字串,通常被用來檢索、替換那些符合某個模式(規則)的文字。正規表示式的核心功能就是處理文字。正規表示式並不僅限於某一種語言,但是在每種語言中有細微的差別。

二、re模組

  在 Python 中提供了 re 模組來使用正規表示式。每一個字串都可以視為一個簡單的正規表示式。

re.findall(pattern, string, flags=0)            # 去整個文字中查詢所有符合正規表示式的文字
re.search(pattern, string, flags=0)             # 去整個文字中去匹配,返回匹配成功的第一個
re.match(pattern, string, flags=0)              # 從開始位置開始匹配,返回匹配成功的第一個
re.split(pattern, string, maxsplit=0, flags=0)  # 根據正規表示式切割字串
re.Match.group([group1, ...])                   # 返回這個匹配物件或者特定子組
re.Match.groups(default=None)                   # 返回一個唯一或者全部子組的元組

  在 Python 中,主要有兩種方法完成模式匹配:“搜尋”(searching),即在字串任意部分中搜尋匹配的模式;而 “匹配”(matching)是指判斷一個字串能否從起始處全部或者部分匹配某個模式。搜尋透過 search() 函式或方法來實現,而匹配是呼叫 match() 函式或方法實現。

  match() 函式試圖從字串的起始部分對模式進行匹配。如果匹配成功,就返回一個匹配物件(Match 物件);如果匹配失敗,就返回 None,匹配物件(Match 物件)的 group() 方法能夠用於顯示那個成功的匹配。

  search() 的工作方式與 match() 完全一致,不同之處在於 search() 會用它的字串引數,在任意位置對給定正規表示式模式搜尋第一次出現的匹配情況。如果搜尋到成功的匹配,就會返回一個匹配物件(Match 物件);否則,返回 None。

  由此可見,match() 試圖從字串的起始部分開始匹配模式,而 search() 函式不但會搜尋模式在字串中第一次出現的位置,而且嚴格地對字串從左到右搜尋。

  findall() 查詢字串中某個正規表示式模式全部的非重複出現情況。這與 search() 在執行字串搜尋時類似,但與 match() 和 search() 的不同之處在於,findall() 總是返回一個列表。如果 findall() 沒有找到匹配的部分,就返回一個空列表,但如果匹配成功,列表將包含所有成功的匹配部分(從左到有按出現順序排列)。

import re

content = "你好啊,小櫻同學!歡迎你加入小櫻班。從現在開始你就是我的朋友啊。小櫻同志,請多多關照。"
regex = "小櫻"

result = re.findall(regex,content)
print(result)


result = re.search(regex,content)
print(result.group())

result = re.match(regex,content)
print(result)

result = re.split(r"[,!。]",content)
print(result)

三、基礎語法

3.1、跳脫字元

  使用正規表示式去檢索某些特殊字元的時候,需要用到跳脫字元,否則檢索不到結果,甚至會報錯;在 Python 中,\ 具有轉義的意思,會對緊隨其後的字元進行轉義,如果我們想使用普通的 \ ,需要在使用一個 \ 對它進行轉義。

import re

content = "abc$def(123(456))"
regex = "\(456"

result = re.findall(regex,content)
print(result)

result = re.match(regex,content)
print(result)

需要用到轉義符號的常見字元如下:. * + ( ) $ / \ ? [ ] ^

3.2、字元匹配符

字元匹配符 含義 例項 解釋
[] 可接收的字元列表 [abc] a、b、c 中的任意 1 個字元
[^] 不可接收的字元列表 [^abc] 除 a、b、c 之外的任意 1 個字元
包括數字和特殊符號
- 連字元 a-z 任意一個小寫字母
import re

content = "abc123def4567AbC"

result = re.findall("[abc]",content)
print(result)

result = re.findall("[^abc]",content)
print(result)

result = re.findall("[a-z]",content)
print(result)

3.3、元字元

元字元 含義
. 匹配單個除換行符以外的任意字元
\d 匹配 0~9 任意一個數字
\D 匹配單個任意非數字字元
\s 匹配任意空白字元
\S 匹配任意不是空白符的字元
\w 匹配字母或數字或下劃線的任意字元
\W 匹配任意不是字母、數字、下劃線的字元
import re

content = "abc123def4567AbC"

result = re.findall("\\d\\d\\d",content)
print(result)

result = re.findall(r"\d\w",content)
print(result)

元字元的大寫表示不匹配;

3.4、重複限定符

  重複限定符用於指定其前面的字元和組合項連續出現多少次。

重複限定符 意義
? 0 次 或 1 次
* 0 次 或 多次
+ 1 次 或 多次
{n} 正好出現 n 次
{n,} 至少出現 n 次
{n,m} 出現 n 次 至 m 次
import re

content = "abc123def4567AbC89d115200a1"

resutl = re.findall(r"\d{3,5}",content)
print(resutl)

result = re.findall(r"\d+",content)
print(result)

3.5、定位符

  定位符,用來指定要匹配的字串出現的位置。

定位符 含義
^ 指定起始字元
$ 指定結束字元
\b 匹配目標字串的邊界,
邊界指的是字串間有空格,或者目標字串的結束位置
\B 匹配非單詞邊界
import re

content = "abc123 def4567abc123abc abc89 d115200 a1abc"

result = re.findall("^abc",content)
print(result)

result = re.findall("abc$",content)
print(result)

result = re.findall(r"abc\b",content)
print(result)

result = re.findall("^ab\B",content)
print(result)

3.6、選擇匹配符

  正規表示式用符號 | 來表示或,也叫做分支條件,當滿足正則表達裡的分支條件的任何一種條件時,都會當成匹配成功。

import re

content = "你好啊,小櫻同學,歡迎你加入小櫻班,從現在開始你就是我的朋友啊,小櫻同志,請多多關照。"
regex = "小櫻同學|小櫻同志"

result = re.findall(regex,content)
print(result)

3.6、分組組合

  重複限定符是作用在與它相鄰的最左邊的一個字元。正規表示式中可以使用小括號 () 來做分組,也就是括號中的內容會作為一個整體。

3.6.1、捕獲分組

  我們可以使用 group() 方法匹配物件方法,從一個分組中獲取匹配的文字。正規表示式字串中的第一對括號是第 1 組,第二對括號是第 2 組,依次類推。向 group() 匹配物件方法傳入整數就可以取得匹配文字的不同部分。向 group() 傳入 0 或者不傳入引數,將返回這個匹配的文字。

  如果想要一次就獲取所有的分組,我們可以使用 groups() 方法。

捕獲分組 說明
(pattern) 非命名捕獲。捕獲匹配的子字串。編號為零的第一個捕獲是由整個正規表示式模式匹配的文字,其它捕獲結果則根據左括號的順序從 1 開始自動編號。
(P<name>pattern) 命名捕獲。將匹配的子字串捕獲到一個組名稱或編號名稱中。用於 name 的字串不能包含任何標點符號,並且不能以數字開頭。可以使用單引號替代尖括號。
import re

content = "我是小櫻,我的身份證明是37028419860401232X"
regex = r"\d{6}(\d{4})(\d{2})(\d{2})\d{3}[\dX]"

result = re.search(regex,content)
year = result.group(1)
month = result.group(2)
day = result.group(3)

print(result.group(0))
print(f"{year}-{month}-{day}")
import re

content = "我是小櫻,我的身份證明是37028419860401232X"
regex = r"\d{6}(?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2})\d{3}[\d|X]"

result = re.search(regex,content)
year = result.group("year")
month = result.group("month")
day = result.group("day")

print(result.group(0))
print(f"{year}-{month}-{day}")

3.6.2、非捕獲分組

非捕獲分組 說明
(?:pattern) 匹配 pattern 但不捕獲該匹配的子表示式,即它是一個非捕獲匹配,不儲存以後使用的匹配。
例如:"小櫻(?:同學|同志)" 等價於 "小櫻同學|小櫻同志"
(?=pattern) 它是一個非捕獲匹配。
例如:"Harmony(?=2|3)" 匹配 "Harmony2" 中的 "Harmony",但不匹配 "Harmony1" 中的 "Harmony"
(?!pattern) 該表示式匹配不處於匹配 pattern 的字串的起始點的搜尋字串。它是一個非捕獲匹配。
例如:"Harmony(?=2|3)" 匹配 "Harmony1" 中的 "Harmony",但不匹配 "Harmony2" 中的 "Harmony"
import re

content = "你好啊,小櫻同學,歡迎你加入小櫻班,從現在開始你就是我的朋友啊,小櫻同志,請多多關照。"

regex = "小櫻(?:同學|同志)"
result = re.findall(regex,content)
print(result)

regex = "小櫻(?=同學|同志)"
result = re.findall(regex,content)
print(result)

regex = "小櫻(?!同學|同志)"
result = re.findall(regex,content)
print(result)

3.7、非貪婪匹配

  當 ? 元字元緊隨任何其它限定符 (*、+、?、{n}、{n,}、{n,m})之後,匹配模式是 “非貪婪匹配”。非貪婪匹配搜尋到、儘可能短的字串。而預設的貪婪匹配搜尋到的儘可能長的字串。

import re

content = "abc111111abc"

# 貪婪匹配
result = re.findall(r"\d{3,5}",content)
print(result)

# 非貪婪匹配
result = re.findall(r"\d{3,5}?",content)
print(result)

相關文章