Python - 正規表示式

大家好,未來的Python巫師們!今天,我們將進入Python的正規表示式(regex)的精彩世界。如果你從未聽過regex,也別擔心 - 當你完成這篇教學後,你將能像專家一樣熟練地使用這個強大的工具!

Python - Reg Expressions

正規表示式是什麼?

在我們深入探討之前,先來了解正規表示式是什麼。想像你是一位偵探,試圖在一片文字海洋中尋找特定的模式。正規表示式就像你的放大鏡,幫助你根據模式搜尋和操作字串。很酷,對吧?

原始字串

在Python中處理regex時,我們常常使用原始字串。原始字串以'r'為前缀,並將反斜杠視為文字字符。這在regex中特別有用,因為反斜杠很常見。

# 一般字串
print("Hello\nWorld")
# 原始字串
print(r"Hello\nWorld")

在第一個案例中,你會看到"Hello"和"World"分別在不同的行。在第二個案例中,你會看到"Hello\nWorld"原本的樣子。這在處理regex模式時至關重要。

元字元

元字元是regex的建構塊。它們具有特殊含義,並幫助我們定義模式。讓我們看看一些常見的元字元:

元字元 含義
. 匹配任何字符,除了換行符
^ 匹配字串的開頭
$ 匹配字串的結尾
* 匹配0次或多次重複
+ 匹配1次或多次重複
? 匹配0次或1次重複
{} 匹配明確指定的重複次數
[] 指定要匹配的一組字符
\ 轉義特殊字符

re.match() 函數

re.match() 函數嘗試在字串的開頭匹配模式。如果找到匹配,則返回一個匹配物件;否則,返回None。

import re

result = re.match(r"Hello", "Hello, World!")
if result:
print("找到匹配:", result.group())
else:
print("沒有找到匹配")

這將打印 "找到匹配: Hello"。group() 方法返回匹配的字串。

re.search() 函數

re.match() 只在字串開頭尋找匹配,而 re.search() 則會掃描整個字串尋找匹配。

import re

result = re.search(r"World", "Hello, World!")
if result:
print("找到匹配:", result.group())
else:
print("沒有找到匹配")

這將打印 "找到匹配: World"。

匹配與搜索

match()search() 之間的主要差異在於,match() 只在字串開頭檢查匹配,而 search() 則在字串的任何位置檢查匹配。

re.findall() 函數

re.findall() 函數返回字串中所有不重疊匹配模式的列表。

import re

text = "The rain in Spain falls mainly in the plain"
result = re.findall(r"ain", text)
print(result)

這將打印 ['ain', 'ain', 'ain']

re.sub() 函數

re.sub() 函數將字串中所有匹配模式的出現替換為替換字串。

import re

text = "The rain in Spain"
result = re.sub(r"a", "o", text)
print(result)

這將打印 "The roin in Spoin"。

re.compile() 函數

re.compile() 函數建立一個可重複使用的regex物件,如果你多次使用同一個模式,這可能會更有效。

import re

pattern = re.compile(r"\d+")
result1 = pattern.findall("There are 123 apples and 456 oranges")
result2 = pattern.findall("I have 789 bananas")

print(result1)
print(result2)

這將打印 ['123', '456']['789']

re.finditer() 函數

re.finditer() 函數返回一個迭代器,為字串中所有不重疊匹配模式產生匹配物件。

import re

text = "The rain in Spain"
for match in re.finditer(r"ain", text):
print(f"找到 '{match.group()}' 在位置 {match.start()}-{match.end()}")

這將打印:

找到 'ain' 在位置 5-8
找到 'ain' 在位置 17-20

Python Regex的應用案例

正規表示式有許多實際應用。讓我們看看一個常見的用例:

找出以元音開頭的單詞

import re

text = "An apple a day keeps the doctor away"
vowel_words = re.findall(r'\b[aeiouAEIOU]\w+', text)
print(vowel_words)

這將打印 ['An', 'apple', 'a', 'away']

正規表示式修飾符:選項標誌

Python的re模塊提供了多個選項標誌,這些標誌會修改模式的解釋方式:

標誌 描述
re.IGNORECASE (re.I) 执行大小寫不敏感的匹配
re.MULTILINE (re.M) 使^匹配每行的開頭和$匹配每行的結尾
re.DOTALL (re.S) 使.匹配任何字符,包括換行符
re.VERBOSE (re.X) 讓你可以撰寫可讀性更高的regex模式

正規表示式模式

讓我們探索一些更進階的模式:

字符類別

字符類別允許你指定要匹配的一組字符:

import re

text = "The quick brown fox jumps over the lazy dog"
result = re.findall(r"[aeiou]", text)
print(result)

這將打印文本中所有的元音。

特殊字符類別

Python regex支持特殊字符類別:

類別 描述
\d 匹配任何十進制數字
\D 匹配任何非數字字符
\s 匹配任何空白字符
\S 匹配任何非空白字符
\w 匹配任何字母數字字符
\W 匹配任何非字母數字字符

重複情況

我们可以指定模式應出現的次數:

import re

text = "I have 111 apples and 22 oranges"
result = re.findall(r"\d{2,3}", text)
print(result)

這將打印 ['111', '22'],匹配2或3位數的數字。

非貪婪重複

默認情況下,重複是貪婪的,意味著它會匹配盡可能多的內容。在重複後添加一個?使其變為非貪婪:

import re

text = "<h1>Title</h1><p>Paragraph</p>"
greedy = re.findall(r"<.*>", text)
non_greedy = re.findall(r"<.*?>", text)
print("貪婪:", greedy)
print("非貪婪:", non_greedy)

這將顯示貪婪和非貪婪匹配之間的差異。

使用括號進行分組

括號允許你將regex的部分分組:

import re

text = "John Smith ([email protected])"
result = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", text)
if result:
print(f"全名: {result.group(1)} {result.group(2)}")
print(f"電子郵件: {result.group(3)}")

這從文本中提取名稱和電子郵件。

後向參考

後向參考允許你參考之前匹配的組:

import re

text = "<h1>Title</h1><p>Paragraph</p>"
result = re.findall(r"<(\w+)>.*?</\1>", text)
print(result)

這將匹配開啟和關閉的HTML標籤。

替代選項

|字符允許你指定替代選項:

import re

text = "The color of the sky is blue or gray"
result = re.search(r"blue|gray", text)
if result:
print(f"找到顏色: {result.group()}")

這將匹配"blue"或"gray"。

訂位符

訂位符指定文本中的位置:

import re

text = "Python is awesome"
start = re.match(r"^Python", text)
end = re.search(r"awesome$", text)
print(f"以Python開頭: {bool(start)}")
print(f"以awesome結尾: {bool(end)}")

這會檢查文本是否以"Python"開頭並以"awesome"結尾。

括號中的特殊語法

括號可以用於不只是分組:

  • (?:...) 創建非捕獲組
  • (?P...) 創建命名組
  • (?=...) 創建正向前瞻
  • (?!...) 創建負向前瞻
import re

text = "Python version 3.9.5"
result = re.search(r"Python (?:version )?(?P<version>\d+\.\d+\.\d+)", text)
if result:
print(f"版本: {result.group('version')}")

這會提取版本號,不論"text"中是否存在"version"。

就這樣,各位!我們已經穿越了Python regex的領域,從基礎到一些相當進階的概念。記住,像所有強大的工具一樣,regex需要練習才能掌握。所以,如果你一開始覺得有壓力,也不要氣餒。繼續實驗,你很快就會像專業偵探一樣找到模式!編程愉快!

Credits: Image by storyset