Skip to content

(?:…) — a group that does not count

One line: (?:…) groups a sub-pattern for a quantifier or an alternation without creating a capture, so the groups after it keep their numbers; the same bracket grew a flag slot — (?i:…) — that turns an option on for exactly that group, in every engine here except JavaScript at ES2018.

Level: reference · one construct, eight engines

What it means

Every ( costs something: a number, and in a backtracking engine a little bookkeeping. (?: buys the grouping without the capture. Use it whenever the group exists to hold a quantifier or an alternation and nobody will ask what it matched — which is most groups in most patterns.

The scoped-flag form is the same construct with a flag between ? and :. (?i:ab)c matches ab in either case and c in lowercase only; (?-i:…) turns a flag off inside the group.

Measured

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

construct               python  perl   node  ruby  java  go    rust  fancy
grouping only           abab    abab   abab  abab  abab  abab  abab  abab
does not shift numbers  $1      b      b     $1    b     b     b     b
does not shift, \1      b       \u{1}  \\1   b     1     \\1   \\1   \\1
scoped flag (?i:)       Ab      Ab     n/a   Ab    Ab    Ab    Ab    Ab
scoped flag off (?-i:)  no      no     n/a   no    no    no    no    no

Reading the table

  • Row 1. Quantifying a non-capturing group works exactly like quantifying a capturing one.
  • Rows 2–3, numbering. (?:a)(b) — the b is group 1, because the group before it does not count. $1 gives b in Perl, JavaScript, Java, Go and Rust; \1 gives b in Python and Ruby. The other cells are the replacement spelling story, not a numbering one.
  • Rows 4–5, scoped flags. (?i:a)b on Ab: matches in all seven engines asked, and (?i)a(?-i:b) on AB fails in all seven, because b was made case-sensitive again inside its group. JavaScript is n/a on both rows by design: inline modifier groups arrived in JavaScript after ES2018, and Node 20 (where these tables were made) and Node 24 (where CI checks them) disagree about them, so the driver was told not to ask. As of ES2018 the answer is "refused".

The trap

Forgetting the ?: and then counting groups. A pattern with six brackets where two are (?: has four capture groups, and a $5 in the replacement is an error in Java, empty in Go and Rust, and the literal text $5 in Python and Ruby. The capturing group page has the numbering rule; the fix in any engine larger than a one-liner is names.

Where it is measured in depth

See also