正则表达式:分组、分支与反向引用
分组把多个模式视为一个整体,也可保存匹配到的内容;分支用于表达“满足其中之一”。这些机制是构造复杂规则的核心。
1. 捕获组
功能说明
圆括号 ( ... ) 创建捕获组。它一方面把内部模式作为整体参与数量词或分支,另一方面保存内部匹配的文本,供程序 API、反向引用或替换操作使用。捕获组从左到右编号,从 1 开始。
语法格式
(模式)简单示例
(\d{4})-(\d{2})-(\d{2})测试文本:2026-07-12
匹配结果说明
整体匹配为 2026-07-12。第 1 个捕获组是 2026,第 2 个捕获组是 07,第 3 个捕获组是 12。不同语言返回捕获组的方式不同:Python Match.group(1) 读取第 1 组,PowerShell 成功的标量 -match 可从 $Matches[1] 读取。
2. 非捕获组
功能说明
(?: ... ) 是非捕获组:它只用于分组、限定数量词作用范围或组织分支,不保存内部文本。对于不需要读取结果的括号,优先使用非捕获组可减少组编号变化,也通常更清晰。
语法格式
(?:模式)简单示例
(?:https?://)?example\.com测试文本:example.com http://example.com https://example.com
匹配结果说明
三个地址都能匹配:http 后的 s 可有可无,整个协议部分也可有可无。该括号不会产生捕获组,因此调用 API 时不需要处理无意义的协议组。
3. 命名捕获组
功能说明
命名捕获组为捕获内容赋予语义化名称,避免依赖脆弱的数字编号。JavaScript、.NET 与较新的 PCRE 风格常用 (?<名称>模式);Python 的 re 模块则使用 (?P<名称>模式)。名称规则也会因引擎不同而略有差异。
语法格式
(?<名称>模式)简单示例
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})测试文本:2026-07-12
匹配结果说明
整体匹配为 2026-07-12,命名组 year、month、day 分别保存 2026、07、12。在 Python 中,同一模式应写成 (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})。
4. 或条件:|
功能说明
竖线 | 表示分支选择,即匹配左侧模式或右侧模式。它的作用范围由分组决定;不加分组时,分支可能比预期覆盖更大的区域。Python、ECMAScript 和 .NET 这类回溯引擎通常从左到右接受第一个成功分支,因此重叠分支常把更具体的规则放在前面;POSIX 工具可能采用 leftmost-longest 规则,不能把分支顺序结论无条件搬到 grep。
语法格式
模式A|模式B
(?:模式A|模式B)简单示例
\b(?:cat|dog)\b测试文本:cat dog catalog bird
匹配结果说明
结果是 cat 与 dog。catalog 因为没有右侧单词边界而不匹配。非捕获组确保 \b 同时约束两个候选词。
5. 数字反向引用
功能说明
反向引用可以再次匹配此前捕获组已经匹配到的同一段文本。\1 表示第一个捕获组,\2 表示第二个。它适合查找重复词、成对标签或重复分隔符,但会增加匹配复杂度,不适合不必要地滥用。
语法格式
(捕获内容)\1简单示例
\b(\w+)\s+\1\b测试文本:the the cat dog dog
匹配结果说明
结果是 the the 与 dog dog。第一个捕获组先捕获一个单词,\1 要求后面出现完全相同的单词;cat dog 两个单词不同,不匹配。若需忽略大小写,可配合 i 标志。
6. 命名反向引用
功能说明
命名反向引用通过组名引用前面已捕获的内容,可读性优于 \1。JavaScript 常用 \k<名称>,Python 常用 (?P=名称);使用前需要确认引擎支持的语法。
语法格式
(?<名称>模式)\k<名称>简单示例
^(?<quote>["']).*?\k<quote>$测试文本:"hello" 'hello' "hello'
匹配结果说明
前两项分别匹配,因为开始与结束使用了同一种引号;"hello' 不匹配,因为结尾引号和捕获的 quote 不同。该示例使用 JavaScript 风格;Python 可改写为 ^(?P<quote>["']).*?(?P=quote)$。
练习与自测
- 用命名组提取
level=ERROR latency_ms=2200,再在 Python 中调用groupdict()。 - 比较
^cat|dog$与^(?:cat|dog)$对catalog、hotdog、cat、dog的结果。 - 将日期模式中的不需要读取的括号改成非捕获组,观察组编号是否更稳定。
- 写一个查找连续重复 ASCII 单词的模式,并为大小写敏感与不敏感两种规则分别测试。
- 为什么 RE2 不支持反向引用?什么情况下普通代码中的“先捕获再比较”更易维护?