Skip to content

Regular Expressions — Cheatsheet

Johan Beysen


What Are Regular Expressions?

A regular expression (regex) is a pattern that describes what a string may contain. You use it to search, validate or replace text.

/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i

That pattern matches an email address. Every character has a meaning — this document explains them.

Testing

Use regex101.com to test patterns interactively with an explanation per token.


Anchors

Anchors match a position, not a character.

Pattern Meaning Example match
^ Start of the string ^hello matches hello world
$ End of the string world$ matches hello world
\b Word boundary \bcat\b matches cat but not catch
\B No word boundary \Bcat\B matches concatenate

Character Classes

Pattern Meaning
. Any character except newline
\d Digit — [0-9]
\D Not a digit — [^0-9]
\w Word character — [a-zA-Z0-9_]
\W Not a word character
\s Whitespace — space, tab, newline
\S Not whitespace
\n Newline
\t Tab

Custom Classes with [ ]

[aeiou]       — one of the characters a, e, i, o, u
[a-z]         — lowercase letter a to z
[A-Z]         — uppercase letter A to Z
[0-9]         — digit 0 to 9
[a-zA-Z0-9]   — letter or digit
[^aeiou]      — anything except a vowel  (^ = negation inside [ ])
[a-z&&[^aeiou]] — consonant (Java syntax)

Quantifiers

Quantifiers determine how many times the preceding element may occur.

Pattern Meaning Greedy?
* 0 or more times Yes
+ 1 or more times Yes
? 0 or 1 time (optional) Yes
{n} Exactly n times Yes
{n,} At least n times Yes
{n,m} Between n and m times Yes
*? 0 or more — lazy No
+? 1 or more — lazy No
?? 0 or 1 — lazy No

Greedy vs Lazy

Greedy (default): grabs as many characters as possible. Lazy (with ?): grabs as few characters as possible.

Input:   <b>bold</b> and <i>italic</i>
<.+>     matches:  <b>bold</b> and <i>italic</i>   ← greedy, everything
<.+?>    matches:  <b>   and then   <i>             ← lazy, each tag separately

Groups & References

Pattern Meaning
(abc) Capture group — remembers the match
(?:abc) Non-capturing group — groups without storing
(?P<name>abc) Named capture group (Python/PCRE)
(?<name>abc) Named capture group (JS/.NET)
\1 Backreference to group 1
\k<name> Backreference to a named group
a\|b Alternation — a or b
(https?):\/\/   — capture group 1 = "http" or "https"
(?:https?):\/\/ — same match, but not stored
(\w+)\s\1       — matches a repeated word: "hello hello"

Lookahead & Lookbehind

Matching based on what comes before or after the position — without including that context in the match.

Pattern Type Meaning
(?=abc) Positive lookahead followed by abc
(?!abc) Negative lookahead not followed by abc
(?<=abc) Positive lookbehind preceded by abc
(?<!abc) Negative lookbehind not preceded by abc
\d+(?= euro)      — matches a number only if " euro" follows it
\d+(?! euro)      — matches a number if " euro" does NOT follow it
(?<=€)\d+         — matches a number only if "€" precedes it
(?<!€)\d+         — matches a number if "€" does NOT precede it

Flags / Modifiers

Flags change the behavior of the entire regex.

Flag Meaning
i Case-insensitive
g Global — all matches, not just the first
m Multiline — ^ and $ match per line
s Dotall — . also matches newlines
x Extended — whitespace and comments allowed
/pattern/flags
/hello/gi         // global + case-insensitive
import re
re.findall(r'pattern', text, re.IGNORECASE | re.MULTILINE)
re.IGNORECASE  # i
re.MULTILINE   # m
re.DOTALL      # s
re.VERBOSE     # x
grep -i   # case-insensitive
grep -E   # extended regex (ERE) — +, ?, |, () without backslash
grep -P   # Perl-compatible regex (PCRE)
grep -o   # show only the matched text

Escaping Special Characters

These characters have a special meaning and must be escaped with \ if you mean them literally:

. * + ? ^ $ { } [ ] | ( ) \
3\.14        — matches literally "3.14" (not "3X14")
\(group\)    — matches literally "(group)"
C\+\+        — matches literally "C++"

Common Patterns

Validation

Email:          ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
IPv4:           ^(\d{1,3}\.){3}\d{1,3}$
IPv6 (simple):  ^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$
URL:            https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b
Date (ISO):     ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Time (HH:MM):   ^([01]\d|2[0-3]):[0-5]\d$
Postal code (BE): ^[1-9]\d{3}$
IBAN (BE):      ^BE\d{2}\d{12}$
VAT (BE):       ^BE0\d{9}$

Password Strength

At least 8 characters:                    ^.{8,}$
At least 1 uppercase letter:              ^(?=.*[A-Z])
At least 1 lowercase letter:              ^(?=.*[a-z])
At least 1 digit:                         ^(?=.*\d)
At least 1 special character:             ^(?=.*[!@#$%^&*])
All combined (8+, upper, lower, digit):   ^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$

Text Manipulation

Trim whitespace (start/end):      ^\s+|\s+$
Collapse multiple spaces:         \s{2,}
Remove blank lines:               ^$\n
Remove HTML tags:                 <[^>]+>
Comment (// ...):                 \/\/.*$
Comment (/* ... */):              \/\*[\s\S]*?\*\/

Network & Security

MAC address:               ([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}
Private IP (RFC 1918):     ^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)
Base64:                    ^[A-Za-z0-9+/]+={0,2}$
JWT (3 parts):              ^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$
MD5 hash:                  ^[a-f0-9]{32}$
SHA1 hash:                 ^[a-f0-9]{40}$
SHA256 hash:                ^[a-f0-9]{64}$

Tool-Specific Syntax

grep / egrep

grep  'pattern' file          # BRE — basic regex
grep -E 'pattern' file        # ERE — extended (+ ? | () without \)
grep -P 'pattern' file        # PCRE — Perl-compatible
grep -o '\b[A-Z]{2,}\b' file  # show only the match
grep -n 'pattern' file        # show line numbers
grep -r 'pattern' folder/     # recursive
grep -v 'pattern' file        # lines WITHOUT a match

sed

sed 's/old/new/'          # replace first match per line
sed 's/old/new/g'         # replace all matches
sed 's/old/new/gi'        # case-insensitive + global
sed -n '/pattern/p'       # show only matching lines
sed '/pattern/d'          # delete matching lines
sed 's/\(group\)/[\1]/'   # backreference in BRE
sed -E 's/(group)/[\1]/'  # backreference in ERE

Python

import re

re.match(r'pattern', text)          # match only at the start
re.search(r'pattern', text)         # match anywhere
re.findall(r'pattern', text)        # list of all matches
re.sub(r'old', 'new', text)         # replace
re.split(r'\s+', text)              # split

m = re.search(r'(\d+)-(\d+)', text)
m.group(0)   # full match
m.group(1)   # first group
m.groups()   # tuple of all groups

JavaScript

const re = /pattern/flags;

'text'.match(/pattern/g)               // all matches
'text'.replace(/old/g, 'new')          // replace
'text'.split(/\s+/)                    // split
/pattern/.test('text')                 // true/false

const m = 'text'.match(/(\w+)/);
m[0]   // full match
m[1]   // first group

BRE vs ERE vs PCRE

Feature BRE (grep) ERE (grep -E) PCRE (grep -P)
Groups \(\) () ()
Alternation \| \| \|
+ quantifier \+ + +
? quantifier \? ? ?
Lookahead ✗ ✗ (?=...)
Lookbehind ✗ ✗ (?<=...)
Named groups ✗ ✗ (?P<name>...)
\d \w \s ✗ ✗ ✓