1. 群組 vs. 捕獲群組 vs. 非捕獲群組
群組 (group): 用括號把子表達式包起來以便「成為一個單位」(重複、選擇、優先順序)。
捕獲群組 (capturing group): (…) 會把匹配內容存進 group 編號/名稱,供 group(1)/groupdict() 取用。
非捕獲群組 (non‑capturing group): (?:…) 只做「分組」但不建立群組結果,避免影響編號。
為什麼需要非捕獲群組?
不想要內容被取出(純粹為了分組或量詞/優先順序)。
不想讓群組編號改變(減少「編號漂移」帶來的維護痛苦)。
findall 行為更直覺(不會因為多了捕獲群組而回傳 tuple)。
2. ?
的多重語義
3. 經典數字範式:(\d+(?:\.\d+)?)
好處:小數部分用非捕獲群組,避免
findall
回傳 tuple。
對比看看 捕獲群組版本:
👉 很容易多出 tuple,處理上比較麻煩。
4. 命名捕獲群組
5. Lookaround 斷言
6. findall
的差異
7. 實務範例:抓評分(含 fallback)
或者 三層try~ except~:
def gpt_similarity(text1, text2=user_query):
"""
用GPT判斷兩段文字的相似度
try: 使用re.search分離出整數(浮點數)部分
except: 若捕捉到例外,
再try: re.split()
except: 要是又有例外,
try:使用GPT只輸出數字部分,三層的try~ except~
"""
prompt_compair = f"""
請判斷以下兩段話的語意關聯性,1~10分,並且務必只輸出以下格式:
語意關聯性評分:X分
第一段: {text1}
第二段: {text2}
"""
response = client_LLM.chat.completions.create(
model=model_LLM,
messages=[
{"role": "user", "content": prompt_compair}
]
)
content = response.choices[0].message.content
# '語意關聯性評分:9分'
# pattern = r"語意關聯性評分[::]\s*(\d+)分" #簡單版,假設X為整數
pattern = r"語意關聯性評分[::]\s*(\d+(?:\.\d+)?)\s*分" #連小數點都會捕抓
"""
(?:\.\d+)?
(\d+(?:\.\d+)?) 中的 (?:\.\d+)?:
這裡有兩個「?」但功能不同:
(?: ... ) 裡面的「?:」是「非捕獲群組」的語法記號,
不是量詞。它只代表「分組但不產生 group 編號」。
末尾的「)?」這個「?」才是量詞,表示「整個群組可有可無(0 或 1 次)」。
所以 (?:\.\d+)? 的意思是:
非捕獲群組 (?:\.\d+):一個「小數點 + 至少一位數字」
後面的 ?:讓這整段小數部分變成可選(0 或 1 個)
等價寫法(用捕獲群組也行):
(\.\d+)? 也是「小數部分可選」
"""
try:
match = re.search(pattern, content)
#<re.Match object; span=(0, 10), match='語意關聯性評分:9分'>
return float(match.group(1))
#match.group(0) #'語意關聯性評分:9分'
#match.group(1) #'9'
except:
try:
#有辦法轉為 float 的 元素就return
parts = re.split(":|:|分", content)
parts = re.split(r"[::分]\s*", content) #順便吃掉後面的空白
# re.split(":|:|分", content)
#['語意關聯性評', '', '9', '']
for p in parts:
try:
return float(p.strip().replace(".","."))
# 嘗試轉換成浮點數
except ValueError:
continue
except:
try:
prompt2 = f"請對以下內容,只輸出數字部分:{content}"
response = client_LLM.chat.completions.create(
model=model_LLM,
messages=[
{"role": "user", "content": prompt2}
]
)
content2 = response.choices[0].message.content
return float(content2.strip())
except ValueError:
return 0.0
✅ 小結
( … )
= 捕獲群組,(?: … )
= 非捕獲群組,不影響 group 編號。?
在不同位置代表 可選 / 非貪婪 / 群組前綴,要分清楚。- 數字模式常用
(\d+(?:\.\d+)?)
,避免多餘的 tuple。 findall
的行為強烈受捕獲群組影響 → 不想要 tuple 就用(?:…)
。- 命名群組
(?P<name>…)
與 lookaround(?=…)
等技巧能讓正則更可讀、更穩定。
推薦hahow線上學習python: https://igrape.net/30afN
是否為捕獲組,主要影響 re.findall(),
re.search() 影響不大
1. re.search()
- 會回傳一個 Match 物件。
- 你要什麼內容 → 用
.group(n)
或.group('name')
去取。 - 所以 有沒有捕獲群組,只是影響你
.group()
能取到什麼,不會改變回傳 Match 物件本身。
例子:
👉 search()
的行為沒有變,差別只是你能不能 .group(1)
。
2. re.findall()
- 回傳的是「匹配結果的清單」。
- 如果 pattern 裡有捕獲群組 → 只回傳捕獲內容(如果有多個群組,會變 tuple)。
- 如果沒有捕獲群組 → 回傳整個匹配字串。
例子:
✅ 結論
search()
:回傳的是 Match 物件 → 捕獲群組只是決定你能不能.group(n)
,不會影響回傳型態。findall()
:回傳的是 list → 捕獲群組會直接影響回傳結構(字串 or tuple)。
推薦hahow線上學習python: https://igrape.net/30afN