Regex cheat sheet — the patterns you'll actually use

Regular expressions have a reputation problem. They look like a cat walked across a keyboard — ^[\w.+-]+@[\w-]+\.[a-z]{2,}$ — and most developers respond by copying something from Stack Overflow and praying it works. Fair. But regex isn't magic; it's a small grammar with maybe a dozen rules, and once you know them you can read that cat-walking for what it actually says.

This is the cheat sheet I wish someone had handed me: the building blocks that cover 95% of real-world use, a recipe box of patterns you'll genuinely reuse, and the traps that produce the other 5% of bugs. Bookmark it.

In this post
  • The ~12 building blocks that cover nearly all real regex
  • Character classes, quantifiers, anchors, and groups — with examples
  • A copy-paste recipe box of patterns you'll actually use
  • The greedy/lazy trap, and when not to reach for regex at all

The mental model

A regex is a pattern that describes a set of strings. /cat/ matches any string containing "cat" — "cat", "concatenate", "bobcat". Add anchors and it matches only exactly "cat". The whole game is composing small building blocks into a pattern that matches the strings you want and nothing else.

Most characters match themselves — /hello/ matches "hello". But a dozen characters are metacharacters, and they're the source of both the power and the confusion:

. \ + * ? [ ^ $ { } ( ) | /

To match one literally, escape it: /\./ matches a real period, /what\?/ matches "what?". A surprising share of all regex bugs are just unescaped metacharacters.

Character classes — matching one character

A character class matches one character from a set:

classes.txt
.        any single character (except newline)
[aeiou]  any one vowel
[a-z]    any lowercase letter (a range)
[A-Za-z] any letter, either case
[0-9]    any digit
[^0-9]   anything EXCEPT a digit (^ inside [] means "not")
\d       a digit        — same as [0-9]
\w       a word char    — letters, digits, underscore
\s       whitespace     — space, tab, newline
\D \W \S the negations  — "not a digit", etc.

The [^...] "not" class is quietly one of the most useful — it's how you say "everything up to the next X" without the greedy-matching problems you'll see below.

Quantifiers — how many

A quantifier says how many times the previous thing may repeat:

quantifiers.txt
a*      zero or more a's
a+      one or more a's
a?      zero or one a (optional)
a{3}    exactly three a's
a{2,}   two or more a's
a{2,4}  between two and four a's
The greedy/lazy trap

Quantifiers are greedy by default — they take as much as they can. On the string <a> <b>, the pattern <.*> matches from the first < to the last >, swallowing everything between. Add ? to make it lazy (<.*?>), or better, use a negated class: <[^>]*> — "anything that isn't >" — which is faster and does what you meant.

Anchors — where, not what

Anchors match a position, not a character. They're how you pin a pattern to the start, end, or a word edge:

anchors.txt
^abc     starts with "abc"
abc$     ends with "abc"
^abc$    is exactly "abc"
\bcat\b  the word "cat" — not the "cat" inside "concatenate"

With the m (multiline) flag, ^ and $ match the start and end of each line, not just the whole string. Forgetting to anchor is the classic reason a "validation" regex quietly accepts xyz123abc456.

Groups and alternation

Parentheses group things; the pipe means "or":

groups.txt
gr(a|e)y      "gray" or "grey"
(gray|grey)   same thing, spelled out
(\d{4})-(\d{2})  capture groups — \1 and \2 refer back to them
(?:...)       non-capturing group — group without remembering

Capture groups are what make find-and-replace powerful: match (\d{4})-(\d{2})-(\d{2}) and replace with $2/$3/$1 to turn 2026-07-31 into 07/31/2026. Use (?:...) when you need grouping but don't want to number a group you'll never use.

The patterns you'll actually use

Here's the recipe box. Copy freely — these cover the vast majority of day-to-day matching:

recipes.txt
# email — practical, not RFC-perfect (full email regex is a rabbit hole)
^[\w.+-]+@[\w-]+\.[a-z]{2,}$

# URL
^https?:\/\/[\w.-]+(?:\/\S*)?$

# hex color (#fff or #a1b2c3)
^#?(?:[0-9a-fA-F]{3}){1,2}$

# date, YYYY-MM-DD
\d{4}-\d{2}-\d{2}

# slug (url-friendly identifier)
^[a-z0-9]+(?:-[a-z0-9]+)*$

# semantic version
\d+\.\d+\.\d+(?:-[\w.]+)?$

# UUID
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}

# trim whitespace from both ends
^\s+|\s+$

# IPv4 — naive; allows 999.999.999.999, so validate ranges in code
(?:\d{1,3}\.){3}\d{1,3}

And the flags you'll reach for (JavaScript syntax):

flags.txt
/abc/g   global — find all matches, not just the first
/abc/i   case-insensitive
/abc/m   multiline — ^ and $ match each line
/abc/s   dotall — . matches newlines too

When not to use regex

Part of knowing regex is knowing its limits:

  • Don't parse HTML or XML with regex. They're not regular languages; use a real parser. The famous Stack Overflow answer on this is worth a read.
  • Don't fully validate email with regex. The RFC-perfect pattern is hundreds of characters and still wrong in practice. Use the simple check above, then send a confirmation email.
  • Beware catastrophic backtracking. Nested quantifiers like (a+)+ can hang for seconds on a long non-matching string. Prefer negated classes and keep quantifiers flat.
  • Know your flavor. JavaScript, PCRE (PHP/Python), and grep differ — lookbehind support, Unicode handling, and flag names aren't universal. Test in the engine you'll ship.
Practice where it's safe

regex101 is the best sandbox — it explains your pattern token by token and shows every match live. The MDN regex guide is the reference to keep open.

Wrap-up

A dozen building blocks — classes, quantifiers, anchors, groups — cover nearly everything you'll write. Anchor your validations, prefer negated classes over lazy dots, and keep the recipe box a bookmark away. The cat-walking becomes readable surprisingly fast once you know the alphabet.

Regex shows up everywhere you need precision in text — including the commit-lint rules that keep a git history tidy. If your messages could use that kind of discipline, the commit message generator drafts a conventional one in seconds, and the cheat sheet covers the format. And when the strings you're matching travel through a URL, the URL encoding post explains why they look the way they do.