正则表达式中的分组和引用
分组(Grouping)和引用(Backreference)是正则表达式中最强大的功能之一。它们允许你捕获子字符串、重复复杂模式、引用先前匹配的内容,广泛用于提取数据、验证格式、替换文本等。
1. 捕获组(Capturing Groups)
使用圆括号 () 创建捕获组。每个捕获组会自动编号(从左到右,从1开始),并保存匹配的子字符串。
| 语法 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
(pattern) | 创建一个捕获组 | /(\d{4})-(\d{2})-(\d{2})/ 匹配日期 “2025-12-25” | 整个匹配: “2025-12-25” 组1: “2025” 组2: “12” 组3: “25” |
- 组0:始终代表整个匹配的内容。
- 组1、组2…:对应每个
()的内容。 - 在替换操作中常用:如将 “2025-12-25” 替换为 “12/25/2025″:
$2/$3/$1
2. 非捕获组(Non-capturing Groups)
有时你只需要分组来重复或施加量词,但不想捕获内容。使用 (?:pattern)。
| 语法 | 描述 | 示例 |
|---|---|---|
(?:pattern) | 分组但不捕获,不占用编号 | /https?(://)/ → 只想匹配 “http://” 或 “https://”,但不需要捕获 “://” |
3. 反向引用(Backreferences)
反向引用允许你在同一个正则表达式中引用先前捕获组的内容。
| 语法 | 描述 | 示例 | 匹配说明 |
|---|---|---|---|
\1, \2, … | 引用第1、第2个捕获组的内容 | /^(\w+)=\1$/ | 匹配如 “key=key”,前后完全相同 |
\k<name> 或 (?<name>...) | 命名捕获组(部分引擎支持,如 JS、Python、.NET) | /^(?<word>\w+)=\k<word>$/ | 同上,更清晰 |
经典应用示例:
- 匹配重复单词:
/\b(\w+)\s+\1\b/i
匹配 “the the”、”hello hello” 等重复单词(忽略大小写)。
- 匹配成对的HTML标签(简化版):
/<(\w+)>[^<]*</\1>/
匹配 <div>内容</div>,但不匹配 <div>内容</p>。
- 匹配回文结构(有限):
/^(\d)(\d)\d\2\1$/
匹配如 “12321”、”45554″ 这样的5位回文数字。
4. 命名捕获组(Named Capturing Groups)
更现代、更可读的语法(支持的引擎:JavaScript、Python、Java、.NET、PCRE 等)。
| 语法 | 描述 | 示例 |
|---|---|---|
(?<name>pattern) | 定义命名组 | /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/ |
\k<name> 或 ${name} | 引用命名组 | 在替换中常用 ${year}/${month}/${day} |
在 JavaScript 中提取:
const regex = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/;
const match = "2025-12-25".match(regex);
console.log(match.groups.year); // "2025"
console.log(match.groups.month); // "12"
5. 分组在量词中的应用
分组允许对复杂模式应用量词:
| 示例 | 说明 |
|---|---|
(ab)+ | 匹配 “ab”、”abab”、”ababab” 等 |
(https?://)? | 匹配可选的协议部分 |
(\d{3}-)?\d{8} | 匹配可选区号的电话号码,如 “010-12345678” 或 “12345678” |
6. 实际应用场景总结
| 场景 | 正则示例 | 用途 |
|---|---|---|
| 提取日期组件 | /(\d{4})-(\d{2})-(\d{2})/ | 分离年月日 |
| 验证密码重复 | /^(\S{6,20})$/.test(pwd) && pwd === confirmPwd(结合JS) | 更常用反向引用验证重复内容 |
| 替换日期格式 | str.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1') | 转为 MM/DD/YYYY |
| 查找重复单词 | /(\b\w+\b)(\s+\1)+\b/gi | 高亮文章中重复词 |
| 匹配引号内容 | /["']([^"']*)["']/ 不完美更好: /(["'])([^\\]*(\\.[^\\]*)*)\1/ | 复杂但更准确 |
注意事项
- 过多嵌套分组会降低可读性和性能。
- 不同语言对反向引用的最大组数有限制(如 JS 最多 \1 到 \99)。
- 某些引擎(如旧版 JS)不支持命名组或后瞻内反向引用。
- 反向引用是精确匹配先前捕获的内容(包括大小写)。
分组和引用是让正则从“简单查找”变成“智能解析”的关键工具!如果你有具体语言(如 JS、Python)或实际需求(如解析日志、验证表单),我可以给出更针对性的例子。