Regex in 10 Minutes

Regex looks like a cat walked across the keyboard, but it's five concepts wearing dense syntax. Learn the five, keep the pattern library below bookmarked, and know the two traps (greedy matching, unescaped dots) that cause 90% of regex bugs. The audio explains the thinking — the symbols are here on the page when you need them.

01The model: describe the shape, not the text

A regex describes the shape of text: "three digits, a dash, four digits". The engine slides that description along your string looking for a place it fits. That's the entire mechanism. And the first rule of regex: if you're looking for fixed text, you don't need regex — in / includes() is faster and can't surprise you.

# shape: "digits, dash, digits" — matches phone extensions
\d{3}-\d{4}

"call 555-0199 today"   →  matches  "555-0199"

# but fixed text needs NO regex:
✗ re.search("error", line)     # overkill
✓ "error" in line              # same result, no surprises

02Character classes: what's allowed here

A class answers "what characters may stand in this position?" Three shorthands cover most needs — digit, word-character, whitespace — plus square brackets for a custom allowlist, and a caret inside them for a blocklist.

\d    # one digit            0-9
\w    # one "word" char       letters, digits, _
\s    # one whitespace        space, tab, newline
.     # ANY one character (except newline) — careful, see 07

[abc]     # exactly one of: a, b, or c
[a-f0-9]  # one hex digit (ranges with -)
[^0-9]    # one char that is NOT a digit (^ inside [] = not)

\D \W \S  # capitals = the opposite of their lowercase twin

03Quantifiers: how many of it

A quantifier sits after a thing and says how many times it repeats. Four cover everything: optional, any number, at least one, and exact counts in braces.

colou?r        # ? = 0 or 1     → color, colour
go+al          # + = 1 or more  → goal, gooooal
ab*c           # * = 0 or more  → ac, abc, abbbc
\d{4}          # exactly 4      → 2026
\d{2,4}        # 2 to 4         → 26, 202, 2026
\d{2,}         # 2 or more

# reading practice — a simple date shape:
\d{4}-\d{2}-\d{2}      # 2026-07-22

04The greedy trap: .* eats everything

The most important 90 seconds on this page. Quantifiers are greedy: they grab as much as possible while still letting the match succeed. Combine that with dot-star and you get regex's most classic bug — a match that spans from the first opening to the last closing, swallowing everything between.

text:  <b>bold</b> and <i>italic</i>

<.*>      # greedy: matches  <b>bold</b> and <i>italic</i>  — ALL of it!
<.*?>     # lazy (add ?): matches <b>, then </b>, then <i>... ✓
<[^>]*>   # often better: "anything except the closer" — fast & explicit
The rule of thumb
When your match is way longer than expected, you've been bitten by greed. Fix with the lazy modifier (? after the quantifier) or — usually better — replace the dot with a negated class: "anything except the ending character". The negated class says what you actually mean.

05Anchors: where, not what

Anchors match positions, not characters: start of string, end of string, edge of a word. They fix the sneaky class of bug where your pattern is right but matches in the wrong place — validating "abc123abc" as a number because \d+ found the 123 in the middle.

^\d+$     # ^ start, $ end → the WHOLE string is digits
          # without them: "abc123abc" passes! (finds 123 inside)

\bcat\b   # \b = word boundary → "cat" the word,
          #   not the cat in "category" or "concatenate"

^ERROR    # lines STARTING with ERROR (with multiline flag)
The validation rule
Any regex used to validate user input (is this a valid amount / ID / code?) must be anchored with start and end. Unanchored validation is the bug behind a thousand bypasses: the attacker's string just needs to contain a valid-looking piece. Search = unanchored; validate = anchored. Always.

06Groups: capture the parts you want

Parentheses do two jobs: they bundle (so a quantifier or alternation applies to the whole chunk) and they capture (the matched part comes back to your code, numbered left to right). Alternation — the pipe — means "or".

# bundle + or:
\.(jpg|png|gif)$          # files ending in any of the three

# capture: pull the parts OUT of a date
(\d{4})-(\d{2})-(\d{2})
#  └ group 1: year  └ 2: month  └ 3: day

# in Python:
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
m.group(1)                # "2026"

# swap with backreferences: "Lastname, First" → "First Lastname"
re.sub(r"(\w+), (\w+)", r"\2 \1", "Lovelace, Ada")

07The escaping trap: the dot that matched too much

Fifteen characters have special meanings. Use one literally and forget the backslash, and your pattern silently matches more than you meant — the classic being the unescaped dot in domains and prices, which means "any character at all".

# special characters (escape with \ to mean them literally):
.  *  +  ?  (  )  [  ]  {  }  ^  $  |  \  /

swiftgrasp.com     # the . matches ANY char:
                   #   also matches "swiftgraspXcom" ⚠
swiftgrasp\.com    # ✓ a literal dot

$19.99             # $ means "end of string" — never matches!
\$19\.99           # ✓ literal dollar, literal dot
Why this bug survives so long
An unescaped dot still matches the correct text too — swiftgrasp.com does match "swiftgrasp.com". Your tests pass. It just also matches things it shouldn't, and you find out in production, from a weird edge case. Silent over-matching is why regexes need negative tests: assert what must not match.

08The pattern library: copy, paste, adapt

The patterns everyone actually needs, tested and annotated. One honest note first: the "perfect email regex" is a famous trap — the spec allows monstrosities, so production systems use a simple pattern plus a confirmation email. Pragmatism beats spec-lawyering.

# email (pragmatic — pair with a confirmation mail)
^[\w.+-]+@[\w-]+\.[\w.]+$

# URL (http/https)
https?://[^\s]+

# ISO date  2026-07-22
\b\d{4}-\d{2}-\d{2}\b

# time  14:30 or 9:05
\b\d{1,2}:\d{2}\b

# number with optional decimals (prices, amounts)
-?\d+(\.\d+)?

# IPv4 (pragmatic)
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b

# collapse repeated whitespace → single space
\s+        # replace with " "

# extract everything between quotes
"([^"]*)"

# hex color  #fff or #1a2b3c
#[0-9a-fA-F]{3,6}\b

09When NOT to use regex

The most senior regex skill is declining to use it. Three honest rules: fixed text needs no regex; nested structures (HTML, JSON, code) can't be parsed with regex — that's a mathematical limit, not a skill issue — use a real parser; and if the pattern doesn't fit on one line, future-you will curse present-you. Regex is a scalpel, not a chainsaw.

✗ parsing HTML with regex     # use an HTML parser (BeautifulSoup...)
✗ parsing JSON with regex     # use json.loads — it exists, it's right there
✗ a 200-char regex            # split into steps, or write a small parser

✓ log line extraction         # regex's home turf
✓ validation (anchored!)      # with negative tests
✓ find & replace in editor    # daily superpower
The tooling that changes everything
Never debug a regex by staring at it. Paste it into regex101.com — it explains every symbol, shows matches live, and lets you build a test set. Two minutes there beats twenty minutes of squinting. (And every editor's find-replace accepts regex — the daily superpower most people never turn on.)

10Cheat sheet

Everything above, compressed.

# classes                      # quantifiers
\d digit    [abc] one of       ?  0-1        *  0+
\w word     [a-z] range        +  1+         {n,m} counted
\s space    [^x]  not x        add ? after → lazy version

# anchors                      # groups
^  start    $  end             (x)  capture
\b word edge                   (a|b) or
validate = ^...$ always        \1 backreference

# the two traps
.* too greedy   → .*? or [^X]*
literal . $ + ? → escape: \. \$ \+ \?

# debug
regex101.com — explain, test, save

# decline politely
fixed text → in/includes · HTML/JSON → real parser

That completes the beginner developer stack: PythonGitcommand lineSQLHTTPDocker → regex. Seven pages, about seventy minutes of audio, and a working vocabulary for your first year of building things. Go build one.