共计 1761 个字符,预计需要花费 5 分钟才能阅读完成。
在前面的学习中,我们已经了解了正则表达式的基本匹配与常用符号,如 .、*、+、?、[]、{} 等等。今天我们将进一步学习正则表达式中的 分组与反向引用,这是在处理复杂字符串匹配时非常有用的高级技巧。
一、分组的基本概念
在正则表达式中,使用小括号 () 可以将一部分模式包裹起来,这样它就成为一个“分组(group)”。
例如:
import re
pattern = r"(ab)+"
text = "abababab"
result = re.match(pattern, text)
print(result.group()) # 输出 abababab
这里 (ab)+ 表示匹配一个或多个 ab 组合。
二、提取分组内容
分组的另一个用途是提取匹配的部分。通过 group()、groups() 方法可以获得不同的分组内容:
pattern = r"(\d{4})-(\d{2})-(\d{2})"
text = "2025-10-14"
match = re.match(pattern, text)
if match:
print(match.group(0)) # 整个匹配:2025-10-14
print(match.group(1)) # 第一个分组:2025
print(match.group(2)) # 第二个分组:10
print(match.group(3)) # 第三个分组:14
print(match.groups()) # 返回所有分组内容的元组 ('2025', '10', '14')
分组编号从 1 开始,group(0) 始终表示完整匹配的字符串。
三、命名分组
为了提高可读性,可以为分组命名,使用 (?P<name>pattern) 的语法。
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
text = "2025-10-14"
match = re.match(pattern, text)
if match:
print(match.group("year")) # 输出 2025
print(match.group("month")) # 输出 10
print(match.group("day")) # 输出 14
命名分组在解析复杂文本时尤其有用,代码可读性更强。
四、反向引用
反向引用是指在正则表达式中再次引用之前的分组内容。
例如,想要匹配连续重复的单词:
pattern = r"(\b\w+)\s+\1"
text = "hello hello world"
match = re.search(pattern, text)
if match:
print(match.group()) # 输出 hello hello
这里的 \1 表示引用第一个分组的内容,即第一个单词。
五、命名分组的反向引用
如果使用命名分组,可以使用 (?P=name) 进行引用:
pattern = r"(?P<word>\b\w+)\s+(?P=word)"
text = "bye bye birdie"
match = re.search(pattern, text)
if match:
print(match.group()) # 输出 bye bye
这种写法在阅读时更加直观。
六、嵌套分组与编号规则
如果存在嵌套分组,例如 ((ab)c),则编号顺序是从左到右、从外到内依次分配。
pattern = r"((ab)c)"
text = "abc"
match = re.match(pattern, text)
if match:
print(match.groups()) # 输出 ('abc', 'ab')
第一个分组是整个 ((ab)c) 的结果,第二个分组是内部的 (ab)。
七、实际应用示例
下面演示如何匹配 HTML 标签中的内容:
pattern = r"<(\w+)>(.*?)</\1>"
text = "<title>Python 教程 </title>"
match = re.search(pattern, text)
if match:
print(match.group(1)) # 输出 title
print(match.group(2)) # 输出 Python 教程
其中 \1 确保结束标签与开始标签相同。
八、小结
分组与反向引用让正则表达式的匹配与提取功能更加强大。
()用于分组\1,\2等用于反向引用(?P<name>...)定义命名分组(?P=name)引用命名分组
掌握这些技巧后,你将能够轻松处理复杂的文本结构匹配任务。