正则表达式:分组、分支与反向引用

分组把多个模式视为一个整体,也可保存匹配到的内容;分支用于表达“满足其中之一”。这些机制是构造复杂规则的核心。

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)$。

练习与自测

  1. 用命名组提取 level=ERROR latency_ms=2200,再在 Python 中调用 groupdict()。
  2. 比较 ^cat|dog$ 与 ^(?:cat|dog)$ 对 catalog、hotdog、cat、dog 的结果。
  3. 将日期模式中的不需要读取的括号改成非捕获组,观察组编号是否更稳定。
  4. 写一个查找连续重复 ASCII 单词的模式,并为大小写敏感与不敏感两种规则分别测试。
  5. 为什么 RE2 不支持反向引用?什么情况下普通代码中的“先捕获再比较”更易维护?

上一节:边界与数量词|下一节:先行与后行断言。