Regular Expressions Cheatsheet

Alternation

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

The | operator

| splits the pattern into alternatives and has the lowest precedence of every regex operator, so it stretches as far left and right as it can.

PatternReads as
^cat|dog$(^cat) or (dog$)
^(?:cat|dog)$start, then cat or dog, then end
gray|greytwo whole words
gr(?:a|e)ysame thing, one shared prefix
abc|abc or the empty string, always matches
import re
re.findall(r'^a|b$', 'ab')       # ['a', 'b'], two separate matches
re.search(r'(?:^a|b)$', 'ab')    # matches 'b' only

Always wrap an alternation in (?:...) when it sits inside a larger pattern. The bug of forgetting is silent, the regex still compiles and still matches something.

Order matters in backtracking engines

Perl-style engines (JavaScript, Python, PCRE, Java, .NET, Ruby, and Go's default) are leftmost-first. They try alternatives left to right and stop at the first one that lets the whole pattern succeed.

re.search(r'cat|category', 'category').group()   # 'cat'
re.search(r'category|cat', 'category').group()   # 'category'

Put the longest or most specific alternative first when the alternatives share a prefix. Anchors also fix it, because a failing anchor forces the engine on to the next branch.

re.search(r'\b(cat|category)\b', 'category').group()   # 'category'

POSIX tools are leftmost-longest instead

grep -E, awk, and sed pick the longest match at the leftmost position, so alternative order does not change the result.

echo category | grep -Eo 'cat|category'   # category
echo category | awk '{ match($0, /cat|category/)
                       print substr($0, RSTART, RLENGTH) }'
# category

Go is leftmost-first by default and only switches with an explicit opt-in.

regexp.MustCompile(`cat|category`).FindString("category")  // "cat"
p := regexp.MustCompilePOSIX(`cat|category`); p.Longest()
p.FindString("category")                                   // "category"

Alternation in BRE

Plain grep and BSD sed use BRE, where there is no alternation at all. GNU tools add \| as an extension, BSD tools do not.

ToolAlternation
grep -E / egrep|
grep (BRE, GNU)|
grep (BRE, BSD/macOS)none, use -E
GNU sed| in BRE, | with -E
BSD sed (macOS)none in BRE, | with -E
awk|, always ERE

Portable answer: pass -E and use bare |. Both GNU and BSD accept -E, and POSIX blesses it.

Building an alternation of literals

For a list of fixed words, a shared prefix plus anchors beats a flat list.

\b(?:cat|dog|bird)\b            fine for a handful
\b(?:jan|feb|mar|apr|may|jun)\b fine, all same length
(?:https?|ftp|file)://          shared suffix outside the group
  • Sort so no alternative is a prefix of a later one, or anchor with \b.
  • Escape every literal you interpolate. re.escape in Python, regexp.QuoteMeta in Go, Pattern.quote in Java.
  • Above roughly a few dozen literals, stop. A set lookup or grep -F -f wordlist is faster and cannot be broken by a stray metacharacter.
# fixed-string search over a word list, no regex engine involved
grep -F -f words.txt access.log

Empty and overlapping alternatives

An empty branch makes the whole group optional and can produce zero-width matches everywhere.

RiskyMeaningBetter
(a|b|)a, b, or nothing(?:a|b)?
(?:x|)+can loop on emptyx*
(a|a)duplicate work on failurea
(.*|\d+)second branch unreachablepick one

Duplicate or overlapping branches are the raw material for catastrophic backtracking, because on failure the engine retries the same span through every branch. (a|a)*$ against a long run of a followed by b is the classic exponential case.