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.
| Quantifier | Repeats |
|---|---|
* | 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.
| Greedy | Lazy |
|---|---|
* | *? |
+ | +? |
? | ?? |
{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.
| Form | Meaning |
|---|---|
*+ ++ ?+ {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.
| Risky | Safer |
|---|---|
(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}withmabove 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 texta{1, 3}. - In BRE (
grepwithout-E,sedwithout-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.