{n,m}: bounded repetition between two counts
{n,m} matches between n and m repetitions of the preceding token, inclusive at both ends. It prefers the maximum unless you make it lazy with a trailing question mark.
At a glance
- Syntax
- {n,m}
- Bounds
- inclusive at both ends
- Greedy
- yes, it starts at m and gives characters back
- Lazy form
- {n,m}?, which starts at n and takes more only if forced
- No {,m} shorthand
- /a{,3}/ is literal text, not zero to three
- Why it matters
- bounding a quantifier is the cheapest defence against runaway backtracking
In detail
Both ends are inclusive, so {2,4} accepts two, three or four repetitions and nothing else. Being greedy it tries four first and gives characters back only when the rest of the pattern fails, which is why /a{2,4}/ against a run of four a characters returns all four while the lazy /a{2,4}?/ returns the minimum two.
There is no lower bound shorthand in JavaScript. Other engines accept {,3} to mean zero to three; here the sequence is not a valid quantifier, so it falls through to literal text and the pattern searches for the characters a{,3}. It compiles without complaint, which makes it one of the more expensive typos in this area.
This is the quantifier to reach for when hardening a pattern. Every unbounded repeat is a search space with no ceiling, and every real world field has one: a postcode is not two hundred characters and a year is not nine digits. Converting + to {1,12} costs a moment and removes an entire category of pathological input.
Bounded counts also express real formats directly. A two to four digit year fragment, a three to six character country segment, a password of at least eight and at most sixty four characters: writing the bounds in the pattern is both the check and the documentation, and it fails loudly when the requirement changes.
Examples
["1", "12", "1234", "12345"].map((s) => /^\d{2,4}$/.test(s));
// [false, true, true, false]
"aaaa".match(/a{2,4}/)[0]; // "aaaa", greedy
"aaaa".match(/a{2,4}?/)[0]; // "aa", lazy/a{,3}/.test("a"); // false
/a{,3}/.test("a{,3}"); // true, the braces are literal textThings that catch people out
- {,3} is not zero to three in JavaScript. It is literal text, and the pattern quietly stops checking anything you thought it was checking.
- The bounds are inclusive, so {2,4} accepts four repetitions. Writing {2,3} when you meant up to and including three is an off by one that only shows up on maximum length input.
- A bounded quantifier is still exponential when nested inside another one. Bounding helps because it caps the size, not because it removes the nesting.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →{n}: matching exactly n repetitions
An exact count is the clearest quantifier there is, until it is applied to a group, at which point the capture keeps only the final iteration.
Lazy quantifiers: *?, +?, ?? and {n,m}?
A trailing question mark makes a quantifier prefer the shortest match. It fixes the overreaching tag pattern, and a negated class often fixes it better still.
Catastrophic backtracking: why a regex freezes a page
Nested quantifiers over overlapping characters make the work grow exponentially with input length. Here is the shape that causes it and the rewrites for it.
Atomic groups in JavaScript: emulating them with a lookahead
JavaScript has no (?>...) and no possessive quantifiers. The lookahead and backreference pair gives you the same no backtracking behaviour.
The ^ anchor: start of string, or start of line
Where the caret matches with and without the m flag, why it consumes nothing, and the entirely different job it does inside a character class.
The $ anchor: end of string, and the trailing newline question
Where $ matches in JavaScript, why it refuses to match before a trailing newline when Python and Ruby allow it, and how to write a literal dollar sign.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links