{n}, {n,}, {n,m} — counted repetition, and the two spellings that are not the same everywhere¶
One line: a{2}, a{2,} and a{1,2} mean exactly, at-least and between in all eight engines; a{,2} (no lower bound) is up-to-two in Python, Perl, Ruby and fancy-regex, a literal a{,2} in JavaScript and Go, and a compile error in Java and Rust; and a bare { that is not a quantifier is a literal in six engines and refused by Java and Rust.
Level: reference · one construct, eight engines
What it means¶
| Form | Repetitions | Greedy |
|---|---|---|
x{n} |
exactly n | — |
x{n,} |
n or more | yes |
x{n,m} |
between n and m | yes |
x{,m} |
zero to m — where it is supported | yes |
Like * and +, a counted quantifier is greedy, takes a ? suffix to become lazy and a + suffix to become possessive, and applies to the single atom before it.
Measured¶
Verified output of kw_braces_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
construct python perl node ruby java go rust fancy
exactly two aa aa aa aa aa aa aa aa
two or more aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
between one and two aa aa aa aa aa aa aa aa
{,2} up to two aa aa no aa - no - aa
{2,1} backwards - no - - - - - -
{1001} large count ok ok ok ok ok - ok ok
{2}{3} stacked - - - aaaaaa aa - aaaaaa no
literal brace a{ a{ a{ a{ - a{ - a{
literal brace, escaped a{ a{ a{ a{ a{ a{ a{ a{
Reading the table¶
- Rows 1–3 are unanimous. Row 3 is worth a second look:
a{1,2}onaaaamatchesaa, the greedy maximum, not the whole run. - Row 4,
a{,2}. Python, Perl, Ruby andfancy-regexread it asa{0,2}and matchaa. JavaScript and Go read it as the literal texta{,2}and answerno— no error, no match, a pattern that is silently a different pattern. Java and Rust'sregexrefuse it. Write{0,2}; every engine agrees about that. - Row 5,
a{2,1}. Backwards bounds are refused everywhere — except Perl, which compiles the pattern and answersno. Perl 5.42 accepts{2,1}as a quantifier that can never be satisfied. - Row 6,
a{1001}. Go refuses: RE2's documented limit is 1000. Everyone else compiles it. A pattern that validates a fixed-width field of 1024 characters is portable to seven of these eight engines. - Row 7,
a{2}{3}. Stacked counts are refused by Python, Perl, JavaScript and Go. Ruby and Rust'sregexcompose them —(a{2}){3}— and match sixas. Java matches two: it accepts the syntax and the second count does nothing visible here.fancy-regexcompiles it and matches nothing at all. Four different answers to a pattern nobody should write. - Rows 8–9, a bare
{.a{is a literal in six engines; Java and Rust'sregexrefuse it.a\{is a literal everywhere. Escape the brace.
The trap¶
Row 4 is the one that ships. {,n} is a natural thing to write, it is legal Python and legal Perl, and in JavaScript or Go it turns the quantifier into five ordinary characters. A test that never exercises the "zero occurrences" case passes anyway.
Where it is measured in depth¶
- Catastrophic backtracking —
([a-z]{2,4})+as a shape that explodes. - Greedy and lazy —
{n,m}?.
See also¶
*,+,?— the uncounted forms.- Lazy
{n,m}?· Possessive{n,m}+. - Portability between engines.