Regular Expressions Cheatsheet

Quantifiers

Use this Regular Expressions reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Greedy quantifiers

A quantifier applies to the single token before it, which is one char, one class, one group, or one escape. Greedy is the default, meaning it takes as much as it can and then gives characters back only if the rest of the pattern fails.

QuantifierRepeats
*0 or more
+1 or more
?0 or 1
{n}exactly n
{n,}n or more
{n,m}n to m inclusive
{,m}0 to m (PCRE, Python). Literal text in JS, a compile error in Java

{0,} is *, {1,} is +, and {0,1} is ?. There is no {,} form.

Pattern: <.+>      Input: <a><b>     Match: <a><b>   (greedy, one match)
Pattern: <.+?>     Input: <a><b>     Match: <a>      (lazy)
Pattern: <[^>]+>   Input: <a><b>     Match: <a>      (negated class, fastest)

Lazy quantifiers

Append ? to make a quantifier take as little as possible, then expand only when the rest fails.

GreedyLazy
**?
++?
???
{n,m}{n,m}?

Lazy is the right tool for a delimited span, such as HTML-ish tags or quoted strings, but a negated class is usually faster because it never has to backtrack.

import re
re.findall(r'"(.*?)"', 'say "hi" and "bye"')   # ['hi', 'bye']
re.findall(r'"(.*)"',  'say "hi" and "bye"')   # ['hi" and "bye']

Possessive quantifiers

Append + to forbid backtracking entirely. Once the quantifier has consumed, it never gives anything back, so a failing match fails immediately instead of exploring.

FormMeaning
*+ ++ ?+ {n,m}+greedy, no backtracking
(?>...)atomic group, same effect for a whole subpattern

Available in PCRE/PHP, Java, Ruby 2.0+, and .NET. Not in JavaScript, Python re (use the regex package), or Go RE2.

\d++abc   on "111111111111111" fails at once
\d+abc    on "111111111111111" tries every split first

Possessive quantifiers change what matches, not just the speed. ".*+" never matches "hi" because .*+ eats the closing quote and refuses to release it.

What the quantifier binds to

abc+      -> ab then one or more c
(abc)+    -> one or more "abc"
[abc]+    -> one or more of a, b, or c
\d{3}     -> three digits
ab{2,3}   -> a then two or three b
(?:ab){2} -> "abab"

Wrap in a non-capturing group (?:...) when you want to repeat a sequence but do not need the capture. Repeating a capture group keeps only the last iteration in the group, so (\d)+ on 123 leaves group 1 as 3, not 123. Capture the whole run instead: (\d+).

Nullable quantifiers and empty matches

A quantifier over something that can match empty is a trap. (a*)* and (\s*)+ can loop on a zero-width match, and different engines respond differently. Most modern engines break the loop, some older ones hang.

RiskySafer
(a*)*a*
(\s*)+\s*
(a|b|)+(a|b)*
^(\w*,?)*$^\w*(,\w*)*$

An empty-capable pattern also produces empty matches in global scans. re.findall(r'\d*', 'a1b') returns ['', '1', '', ''], and the same pattern in a JavaScript matchAll yields the same empty entries. Require at least one char (\d+) when you are scanning.

Counting gotchas

  • {n,m} with m above the engine cap fails to compile. PCRE2 caps at 65535 and Go RE2 at 1000. Python, JavaScript, and Java accept far larger counts, they just get slow.
  • A space inside braces makes the whole thing literal in most flavors, so a{1, 3} matches the text a{1, 3}.
  • In BRE (grep without -E, sed without -E) the braces must be escaped: a\{2,3\}, and + and ? need escaping too.
  • Repetition is per code unit, not per grapheme. .{3} on a family emoji counts surrogate pairs and joiners, not the visible character.