Skip to content

*, +, ? — zero or more, one or more, at most one

One line: The three basic quantifiers apply to the atom before them, are greedy, and behave identically in all eight engines — including the two surprises, that x* succeeds on a subject with no x in it by matching the empty string, and that x** is a compile error in six engines and legal in Ruby and Rust.

Level: reference · one construct, eight engines

What it means

Quantifier Matches the atom Same as
x* zero or more times x{0,}
x+ one or more times x{1,}
x? zero or one time x{0,1}

All three are greedy: they take as many repetitions as they can and give them back one at a time if the rest of the pattern fails. The atom is the single thing before the quantifier — a character, a class, an escape, or a group. ab* is a then any number of b; (ab)* is any number of ab.

Measured

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

construct                 python  perl  node  ruby  java  go    rust  fancy
b* takes nothing          ac      ac    ac    ac    ac    ac    ac    ac
b+ needs one              no      no    no    no    no    no    no    no
b? takes at most one      no      no    no    no    no    no    no    no
* on a subject with none  ""      ""    ""    ""    ""    ""    ""    ""
? is greedy too           a       a     a     a     a     a     a     a
stacked quantifiers x**   -       -     -     xx    -     -     xx    -
quantifier on nothing     -       -     -     -     -     -     -     -
quantified group          abab    abab  abab  abab  abab  abab  abab  abab

Reading the table

  • Rows 1–3. b* accepts nothing between a and c; b+ insists on at least one; b? refuses two. Unanimous, as this part of the syntax has been for forty years.
  • Row 4, x* on abc. Every engine matches — the empty string at offset 0. A search for x* never fails, because zero repetitions is a success. This is the quantifier's most reliable trap: re.findall(r"\d*", "a1b") returns ['', '1', '', ''], and a replace of x* with - puts a dash between every pair of characters. When you mean "some", write +.
  • Row 5, a?a on a. ? is greedy too: it takes the a, the second a fails, it gives the first back, the second a matches. The overall match is a in all eight — but only because the engine backtracked. The lazy ?? on the lazy quantifier page takes nothing first.
  • Row 6, x**. Python, Perl, JavaScript, Java, Go and fancy-regex refuse a quantifier on a quantifier. Ruby and Rust's regex accept it and read x** as (x*)*. Ruby warns; Rust does not. The same is true of a++ — in Ruby that is possessive, in Rust it is (a+)+, and the possessive quantifier page measures what that changes.
  • Row 7, *a. A quantifier with nothing before it is refused everywhere. Row 8 is the group case, (ab)+ taking abab and stopping before the x.

The trap

Row 4. * is the quantifier people reach for when they mean "there might be several", and it also means "there might be none", which turns a search into a guaranteed success and an extraction into a list full of empty strings. The second trap is row 6: x** and x+* pass Rust's compiler and Ruby's, so a stray character in a pattern is a legal pattern there.

Where it is measured in depth

See also