Python - 正規表示式
大家好,未來的Python巫師們!今天,我們將進入Python的正規表示式(regex)的精彩世界。如果你從未聽過regex,也別擔心 - 當你完成這篇教學後,你將能像專家一樣熟練地使用這個強大的工具!
正規表示式是什麼?
在我們深入探討之前,先來了解正規表示式是什麼。想像你是一位偵探,試圖在一片文字海洋中尋找特定的模式。正規表示式就像你的放大鏡,幫助你根據模式搜尋和操作字串。很酷,對吧?
原始字串
在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