Regular expressions have a reputation for looking like line noise — a wall of symbols that seems to mean nothing until you've memorized it. But the underlying idea is simple: a regex is just a pattern that describes what text you're looking for, built up piece by piece from a small set of building blocks. Once you know the blocks, the wall of symbols starts reading like a sentence.
Start with literal characters
The simplest regex is just the text you want to find. The pattern cat matches the literal
characters "cat," wherever they appear. That's already a regex, even without any special symbols.
The dot: match almost anything
A period . matches any single character except a newline. So the pattern c.t
matches "cat," "cot," "c9t," or "c t" — anything with a "c," then any one character, then a "t."
Character classes: match one of a set
Square brackets let you specify a set of acceptable characters for one position. [aeiou]
matches exactly one vowel. [0-9] matches exactly one digit (the hyphen inside brackets defines a
range). You can combine ranges: [a-zA-Z] matches any single letter, upper or lowercase.
A few character classes come up so often they have shorthand:
| Shorthand | Means | Equivalent to |
|---|---|---|
\d | a digit | [0-9] |
\w | a "word" character | [a-zA-Z0-9_] |
\s | whitespace | space, tab, newline |
Capitalizing each of these negates it: \D matches anything that's not a digit, and so on.
Quantifiers: how many times
By default, each part of a pattern matches exactly once. Quantifiers change that:
*— zero or more times.ab*cmatches "ac," "abc," "abbc," "abbbc"...+— one or more times.ab+cmatches "abc," "abbc," but not "ac."?— zero or one time (optional).colou?rmatches both "color" and "colour."{n,m}— between n and m times.\d{3,5}matches 3 to 5 digits in a row.
Putting a few of these together: \d+ matches one or more digits in a row — a whole number, essentially.
Anchors: position, not characters
^ matches the start of the string (or line, with the multiline flag), and $ matches
the end. They don't consume any characters themselves — they just assert a position. The pattern
^https:// matches only if the string literally begins with "https://," not if that text appears
somewhere in the middle.
Groups: treating several characters as one unit
Parentheses ( ) group part of a pattern together, which lets you apply a quantifier to the whole
group rather than just one character, and also lets you extract that specific piece of the match separately
(a "capture group"). (ab)+ matches "ab," "abab," "ababab" — the whole two-character unit
repeating, not just the "b."
Putting it together: matching an email address
A commonly used (if imperfect) email-matching pattern:
\b[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}\b
Read piece by piece:
\b— a word boundary, so we don't match in the middle of a longer word[\w.+-]+— one or more letters, digits, underscores, dots, plus signs, or hyphens (the part before the @)@— a literal @ symbol[\w-]+— the domain name part\.[a-zA-Z]{2,}— a literal dot followed by two or more letters (the top-level domain, like "com" or "org")\b— another word boundary at the end
This is exactly how a seemingly intimidating pattern is actually built: small, individually simple pieces, placed next to each other.
Common flags
g(global) — find all matches in the text, not just the first one.i(ignore case) — match letters regardless of upper or lowercase.m(multiline) — make^and$match the start/end of each line, not just the whole string.
A genuinely useful habit: build patterns incrementally
Nobody writes a complex regex correctly in one attempt, and that's completely normal. Start with the simplest piece of what you're trying to match, test it against real sample text, confirm it works, then add the next piece. A regex tester that highlights matches live as you build the pattern turns this from guesswork into a fast, visual feedback loop.
When not to reach for regex
Regex is great for pattern matching in relatively simple, well-defined text. It's a poor tool for parsing genuinely nested or structured formats like HTML, JSON, or XML — those have rules (matching tags, nested brackets) that regex fundamentally isn't designed to track. If you're tempted to parse JSON or HTML with a regex, use an actual parser instead; you'll save yourself from a long list of edge cases regex simply can't handle correctly.
Frequently Asked Questions
* means “zero or more” of the preceding element, so it matches even if that element isn't present at all. + means “one or more,” requiring at least one occurrence.g) turned on. Without it, most regex engines stop after finding the first match instead of continuing to search the rest of the string.