Skip to content

(?|…) — alternatives that share group numbers

One line: In a branch-reset group, every alternative numbers its capturing groups from the same starting point, so (?|(a)|(b)) has one group — group 1 — whichever branch matched; it exists in Perl and PCRE2 (so ABAP) and nowhere else on this page, and the ordinary (?:(a)|(b)) has two groups, one of which is always unset.

Level: reference · one construct, eight engines

What it means

Alternation and capturing fight each other: a date that is either 2026-09-22 or 22/09/2026 wants "the year" to be one group, but (\d{4})-(\d\d)-(\d\d)|(\d\d)/(\d\d)/(\d{4}) gives the year group 1 on one branch and group 6 on the other. (?|…) resets the counter at each |, so both branches put the year in group 1 — provided the branches are written in the same order.

Without it, the portable answers are a named group per branch with the same name (allowed in Perl, Ruby, Go and fancy-regex, refused by Python, Java and Rust — see named group), or two patterns and an if.

Measured

Verified output of kw_branch_reset_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

construct                python  perl  node  ruby  java  go  rust  fancy
branch reset             -       bb    -     -     -     -   -     -
without it, \1 is unset  no      no    b     no    no    -   -     no
group number after it    -       cb    -     -     -     -   -     -

Reading the table

  • Row 1. (?|(a)|(b))\1 on bb: Perl matches bb. The b branch's group is group 1, and \1 refers to it. Every other engine refuses the syntax.
  • Row 2. (?:(a)|(b))\1 — the same shape without the reset — answers no in Python, Perl, Ruby, Java and fancy-regex: group 1 is on the branch not taken, so it is unset and \1 cannot match. JavaScript answers b, because an unset group there matches the empty string — the backreference page's row 2 again.
  • Row 3. After the branch-reset group, numbering continues from the highest number any branch reached: (?|(a)|(b))(c) on bc with the replacement $2$1 gives cb — (c) is group 2.

The trap

Only the portability one, and it is loud: the syntax is refused everywhere but Perl and PCRE2. The subtler point is inside the feature — the branches must define their groups in the same order for the sharing to mean anything, and nothing checks that they do.

Where it is measured in depth

See also