快速入门
教程
工具和语言
示例
参考
书评
正则表达式教程
简介
目录
特殊字符
不可打印字符
正则表达式引擎内部
字符类
字符类减法
字符类交集
简写字符类
锚点
词边界
交替
可选项
重复
分组和捕获
反向引用
反向引用,第 2 部分
命名组
相对反向引用
分支重置组
自由间距和注释
Unicode
模式修饰符
原子分组
占有量词
前瞻和后顾
环视,第 2 部分
从匹配中保留文本
条件
平衡组
递归
子例程
无限递归
递归和量词
递归和捕获
递归和反向引用
递归和回溯
POSIX 方括号表达式
零长度匹配
继续匹配
本网站上的更多内容
简介
正则表达式快速入门
正则表达式教程
替换字符串教程
应用程序和语言
正则表达式示例
正则表达式参考
替换字符串参考
书评
可打印 PDF
关于本网站
RSS 源和博客
RegexBuddy—Better than a regular expression tutorial!

分支重置组

Perl 5.10 引入了一项称为分支重置组的新正则表达式功能。 JGsoft V2PCRE 7.2 及更高版本也支持此功能,类似于 PHPDelphiR 等具有基于 PCRE 的正则表达式函数的语言。 Boost 在 1.42 版本中将其添加到其 ECMAScript 语法中。

交替在分支重置组内共享相同的捕获组。语法为 (?|regex),其中 (?| 打开组,regex 是任何正则表达式。如果你不使用分支重置组内的任何交替或捕获组,那么它的特殊功能就不会发挥作用。它随后充当非捕获组

正则表达式 (?|(a)|(b)|(c)) 由具有三个交替项的单个分支重置组组成。此正则表达式匹配 abc。该正则表达式只有一个编号为 1 的捕获组,由所有三个交替项共享。匹配后,$1 保存 abc

将此与缺少分支重置组的正则表达式 (a)|(b)|(c) 进行比较。此正则表达式也匹配 abc。但它有三个捕获组。匹配后,$1 保存 a 或什么都不保存,$2 保存 b 或什么都不保存,而 $3 保存 c 或什么都不保存。

反向引用到分支重置组内的捕获组的工作方式符合预期。 (?|(a)|(b)|(c))\1 匹配 aabbcc。由于分支重置组内的备选项只能匹配一个,因此参与匹配的备选项决定了捕获组存储的文本,从而决定了反向引用匹配的文本。

分支重置组中的备选项不必具有相同数量的捕获组。 (?|abc|(d)(e)(f)|g(h)i) 有三个捕获组。当此正则表达式匹配 abc 时,所有三个组都为空。当匹配 def 时,$1 保存 d$2 保存 e$3 保存 f。当匹配 ghi 时,$1 保存 h,而其他两个为空。

分支重置组之前和之后可以有捕获组。分支重置组之前的组按通常方式编号。分支重置组中的组从分支重置组之前的组继续编号,每个备选项重置编号。分支重置组之后的组从具有最多组的备选项继续编号,即使这不是最后一个备选项。因此,(x)(?|abc|(d)(e)(f)|g(h)i)(y) 定义了五个捕获组。 (x) 是组 1,(d)(h) 是组 2,(e) 是组 3,(f) 是组 4,(y) 是组 5。

分支重置组中的命名捕获组

可以在分支重置组内使用命名捕获组。如果这样做,则应为将获得相同编号的组使用相同名称。否则,将在 Perl 或 Boost 中得到不良行为。PowerGREP 将不匹配的组名视为错误。PCRE 仅从版本 8.00 开始可靠地支持分支重置组内的命名组。这意味着 Delphi 仅从 XE7 开始这样做,PHP 仅从版本 5.2.14 开始这样做。

(?'before'x)(?|abc|(?'left'd)(?'middle'e)(?'right'f)|g(?'left'h)i)(?'after'y) 与上一个正则表达式相同。它将五个组命名为“before”、“left”、“middle”、“right”和“after”。请注意,由于第 3 个备选项只有一个捕获组,因此它必须是其他备选项中第一个组的名称。

如果你在某些备选项中省略名称,则这些组仍将与其他备选项共享名称。在正则表达式 (?'before'x)(?|abc|(?'left'd)(?'middle'e)(?'right'f)|g(h)i)(?'after'y) 中,组 (h) 仍被命名为“left”,因为分支重置组使其与 (?'left'd) 的名称和编号相同。

在 Perl、PCRE 和 Boost 中,当你希望不同备选项中的组具有相同的名称时,最好使用分支重置组。这是在 Perl、PCRE 和 Boost 中确保具有相同名称的组实际上是同一个组的唯一方法。

在 PowerGREP 中,具有相同名称的组始终被视为同一个组。因此,在 PowerGREP 中使用带名称的捕获组时,你实际上不需要使用分支重置组。

具有准确天数的日期和月份

是时候进行一个更实际的示例了。这两个正则表达式匹配 m/d 或 mm/dd 格式的日期。它们排除了无效日期,例如 2/31。

^(?:(0?[13578]|1[02])/(3[01]|[12][0-9]|0?[1-9]) # 31 天
 
|  (0?[469]|11)/(30|[12][0-9]|0?[1-9])         # 30 天
 
|  (0?2)/([12][0-9]|0?[1-9])                   # 29 天
 
)$

第一个版本使用一个非捕获组 (?:…)对备选方案进行分组。它有六个单独的捕获组。对于有 31 天的月份,$1$2 保存月份和日期。对于有 30 天的月份,$3$4 保存月份和日期。对于 2 月,$5$6 仅用于 2 月。

^(?|(0?[13578]|1[02])/(3[01]|[12][0-9]|0?[1-9]) # 31 天
 
|  (0?[469]|11)/(30|[12][0-9]|0?[1-9])         # 30 天
 
|  (0?2)/([12][0-9]|0?[1-9])                   # 29 天
 
)$

第二个版本使用分支重置组 (?|…) 对备选项进行分组并合并其捕获组。第 4 个字符是这两个正则表达式之间的唯一区别。现在只有两个捕获组。它们在三个备选项之间共享。找到匹配项时,$1 始终保存月份,$2 始终保存日期,而不管该月份有多少天。