(…) — a group that remembers, numbered by its opening bracket¶
One line: Parentheses group a sub-pattern and capture what it matched; groups are numbered by the position of their ( from left to right, nested ones included; a group inside a quantifier keeps only its last iteration; and a group on the branch not taken is unset, which every engine here reports as an empty string when you ask for it in a replacement.
Level: reference · one construct, eight engines
What it means¶
Three rules that hold in all eight engines:
| Rule | Example | Consequence |
|---|---|---|
| Numbered by opening bracket | ((a)(b)) → 1 = ab, 2 = a, 3 = b |
the outer group is 1, not 3 |
| A quantified group keeps its last iteration | (a|b)+ on ab → group 1 = b |
to get all iterations, loop in the host language |
| A group that did not participate is unset | (a)|(b) on b → group 1 unset |
unset is not "empty" everywhere; see backreference |
What differs is how you refer to a group — $1 against \1 — and that is the replacement references page. The rows below use both spellings so the group semantics can be read in every column.
Measured¶
Verified output of kw_capturing_group_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
construct python perl node ruby java go rust fancy
group repeated abab abab abab abab abab abab abab abab
last iteration, $1 [$1] [b] [b] [$1] [b] [b] [b] [b]
last iteration, \1 [b] [\u{1}] [\\1] [b] [1] [\\1] [\\1] [\\1]
numbered by open bracket $3$2 ba ba $3$2 ba ba ba ba
numbered, \3\2 ba \u{3}\u{2} \\3\\2 ba 32 \\3\\2 \\3\\2 \\3\\2
unset group in replace [$1] [] [] [$1] [] [] [] []
unset group, \1 [] [\u{1}] [\\1] [] [1] [\\1] [\\1] [\\1]
Reading the table¶
- Row 1.
(ab)+takesababand stops at thex— quantifying a group repeats the whole sequence. - Rows 2–3, the last iteration. Both spellings, so every engine gets one it understands:
[$1]in Perl, JavaScript, Java, Go and Rust,[\1]in Python and Ruby. Wherever the reference works, the answer is[b]— the second and last iteration. No engine here keepsa. - Rows 4–5, numbering.
$3$2(or\3\2) on((a)(b))producesbaeverywhere the reference is understood: group 3 is the innerb, 2 is the innera, and the outer group is 1 because its bracket came first. - Rows 6–7, the unset group.
(a)|(b)onb: group 1 never participated. Asked for it in a replacement, every engine with a working reference inserts nothing —[]. The same unset group behaves differently when matched against with\1, which is where JavaScript diverges. - The cells that are not
[b]orbaare the wrong-spelling cells, and they are the replacement references story in miniature: Python and Ruby keep$1as text; Perl turns\1into a control character; Java turns\1into the digit1; Node, Go and Rust keep\1as two characters.
The trap¶
The last-iteration rule. (\d+,)+ over 1,2,3, captures 3, and there is no engine setting that changes that. If you need every iteration, match one unit and iterate in the host language — findall, finditer, scan, FindAllString, captures_iter — and the pattern gets simpler at the same time.
The second trap is numbering after an edit: inserting a group renumbers everything to its right, in the pattern and in every replacement string and every group(3) call that refers to it. Named groups exist for this reason.
Where it is measured in depth¶
- What a backreference is — capturing in order to match the same text again.
- Forward references — what a group holds across iterations, and the engine that clears it.
- Backreferences in the replacement — the
$1that is not a backreference.