Quantifiers +, *, ? and {n}

Say you have the string 1280x720@24-30-60 and you want every number inside it: 1280, 720, 24, 30, 60. Not single digits this time — whole runs of them.

A single digit is pattern:\d. But a “number” is a run of one or more digits stuck together. To say how many of something you want, you attach a quantifier to the pattern.

\d
what to match
{3,5}
how many times
A quantifier is a suffix that describes repetition of whatever comes right before it.

Quantity {n}

The most explicit quantifier is a number in curly braces: pattern:{n}. You write it right after the thing you want to repeat, and it says exactly how many copies you need.

There are a few forms. Here they are with examples.

Exact count: pattern:{5}

pattern:\d{5} means five digits in a row — identical to typing pattern:\d\d\d\d\d, only shorter and easier to read.

alert( "the vault code is 48127".match(/\d{5}/) ); //  "48127"

That pattern happily matches five digits inside a longer run too. If you want a number that is exactly five digits and no more, wrap it in word boundaries: pattern:\b\d{5}\b. The pattern:\b refuses to sit in the middle of a digit run, so 487219 no longer counts.

\d{5} against “487219”
487219
matches “48721”, ignores the trailing 9
\b\d{5}\b against “487219”
487219
no match — the run is six digits, not five
\d{5} matches any five-digit slice; \b\d{5}\b demands the run be exactly five long.

Range: pattern:{3,5} — 3 to 5 times

Put two numbers in the braces to allow a range. pattern:\d{3,5} matches any run of three, four, or five digits:

alert( "only 12 fit, but 4820 remain".match(/\d{3,5}/) ); // "4820"

12 is skipped because two digits is below the lower bound. 4820 fits, so it wins.

Drop the upper number and keep the comma, and you set only a floor. pattern:\d{3,} means “three digits or more, no ceiling”:

alert( "we had 12, now 872015".match(/\d{3,}/) ); // "872015"

Back to 1280x720@24-30-60. A number is one or more digits, which is pattern:\d{1,} — a floor of one, no ceiling:

let str = "1280x720@24-30-60";

let numbers = str.match(/\d{1,}/g);

alert(numbers); // 1280,720,24,30,60

The g flag makes match collect every run instead of stopping at the first. Each separator (the x, the @, and the dashes) breaks the digit run, so you get the five groups you were after.

Try the brace forms yourself. Pick a quantifier for pattern:\d and watch which digit runs light up — notice how {2} grabs exactly two at a time while the range and open-ended forms stretch to fill the run.

interactiveAttach a quantifier to \d and see what matches

Shorthands

Three of these repetition counts show up constantly, so regexp gives each a single-character shorthand. They mean exactly the same as their brace forms — just less typing.

shorthandsame asmeaning
+{1,}one or more
?{0,1}zero or one (optional)
*{0,}zero or more
The three shorthand quantifiers and the brace forms they stand in for.

pattern:+ — one or more

pattern:+ is pattern:{1,}: at least one, then as many as are there. So the number-finding regexp from before shrinks to pattern:\d+:

let str = "1280x720@24-30-60";

alert( str.match(/\d+/g) ); // 1280,720,24,30,60

Same result as pattern:\d{1,}, and it reads better. pattern:+ is probably the quantifier you will reach for most.

pattern:? — zero or one

pattern:? is pattern:{0,1}: the preceding thing may appear once or not at all. It makes that character optional.

Take pattern:https?. The pattern:s? says the s is allowed but not required, so the pattern matches both the plain match:http and the secure match:https:

let str = "Use http or https for the link?";

alert( str.match(/https?/g) ); // http, https
“http”
https?
s skipped
“https”
https
s matched
s? lets the match go through with the s present or absent.

Flip the switch below to see what the pattern:? buys you. With it off, the pattern is a rigid pattern:https and only the secure form lights up. Turn it on and the s becomes optional, so match:http matches too.

interactiveMake the s optional with ?

pattern:* — zero or more

pattern:* is pattern:{0,}: the thing may repeat any number of times, including zero. It is the loosest of the three.

pattern:go* reads as “a g, then any number of os (maybe none)”:

alert( "g go goo".match(/go*/g) ); // g, go, goo

Every one matches, even the lone g, because pattern:o* is satisfied by zero o’s.

Swap pattern:* for pattern:+ and the meaning shifts. pattern:o+ now requires at least one o:

alert( "g go goo".match(/go+/g) ); // go, goo
// g is not matched, because o+ requires at least one o

Toggle between the two below. Both patterns start with pattern:g; the only change is the trailing quantifier on o. Watch the lone g blink in and out of the results as you switch — that single character is the entire difference between pattern:* and pattern:+.

interactiveo* versus o+ on the same string

More examples

Quantifiers are the workhorses of regular expressions — the main building block once patterns get real. A few more to make them stick.

Decimal fractions — a number with a floating point: pattern:\d+\.\d+

alert( "6 40 18.75 2200".match(/\d+\.\d+/g) ); // 18.75

Read it left to right: one or more digits, a literal dot (escaped as pattern:\. so it means an actual period, not “any character”), then one or more digits again. Plain integers like 6, 40, and 2200 have no dot, so they are skipped; only 18.75 fits the shape.

An opening HTML tag with no attributes, like <main> or <p>.

Start with the naive version: pattern:/<[a-z]+>/i

alert( "<main> ... </main>".match(/<[a-z]+>/gi) ); // <main>

It looks for a pattern:<, then one or more Latin letters, then a pattern:>. The i flag makes it case-insensitive so <MAIN> would match too.

That version misses tags like <h2>, because it allows only letters. Per the HTML spec, a tag name may hold a digit anywhere except the first character. So allow a leading letter followed by any mix of letters and digits: pattern:/<[a-z][a-z0-9]*>/i

alert( "<h2>Hey</h2>".match(/<[a-z][a-z0-9]*>/gi) ); // <h2>

Here pattern:[a-z] locks the first character to a letter, and pattern:[a-z0-9]* lets the rest be zero or more letters or digits. The pattern:* (not pattern:+) matters — a one-character tag like <b> has nothing after its first letter, and pattern:* is fine with that.

<[a-z][a-z0-9]*>
< matches <[a-z] matches h[a-z0-9]* matches 2> matches >
Breaking down <[a-z][a-z0-9]*> against the tag <h2>.

Opening or closing tag without attributes: pattern:/<\/?[a-z][a-z0-9]*>/i

To catch closing tags too, allow an optional slash right after the pattern:<. That is pattern:\/? — a slash made optional by pattern:?. The slash is escaped with a backslash because an unescaped / inside a regexp literal would end the pattern early.

alert( "<h2>Hey</h2>".match(/<\/?[a-z][a-z0-9]*>/gi) ); // <h2>, </h2>

Now both <h2> (no slash) and </h2> (with slash) match, since pattern:\/? accepts the slash being present or absent.