Regular expressions have a reputation problem. Most developers meet them through an unreadable line like ^(?:[a-z0-9!#$%&'*+/=?^_{|}~-]+)*$ and conclude the whole topic is arcane. It is not. Regex is a small language with about fifteen concepts, and they layer neatly on top of each other.
This guide teaches those concepts in the order that makes them stick. Open our Regex Tester in another tab and try every example as you read. Regex is learned through the fingers, not the eyes.
What a Regular Expression Is
A regular expression, regex for short, is a pattern that describes a set of strings. You use it to ask three kinds of questions:
- Match: does this text fit the pattern? (validating an email field)
- Find: where does the pattern occur in this text? (finding every phone number in a document)
- Replace: substitute occurrences of a pattern with something else. (reformatting thousands of dates at once)
Nearly every language and tool supports regex: JavaScript, Python, Java, SQL databases, text editors, command-line tools like grep, and even spreadsheet functions. Learn it once and you use it everywhere for the rest of your career.
Level 1: Literal Characters
The simplest regex is just text. The pattern cat matches the letters c, a, t appearing in sequence, anywhere in the input. It matches "cat", "catalog", and "scatter", because all three contain that sequence.
This already reveals the most important regex habit: think about what your pattern matches that you did not intend. Beginners write cat meaning the word cat, and are surprised by "scatter". Most regex bugs are patterns that are too loose, not too strict.
Level 2: Character Classes
Square brackets mean "any one character from this set":
[aeiou]matches one vowel[0-9]matches one digit; the dash denotes a range[a-zA-Z]matches one letter of either case[^0-9]matches one character that is NOT a digit; the caret inside brackets negates
The common sets have shorthand:
\dis a digit, same as[0-9]\wis a "word character": letter, digit, or underscore\sis whitespace: space, tab, or newline- Their capitalized forms negate:
\Dis a non-digit,\Wa non-word character,\Snon-whitespace
The dot . matches any single character except a newline. This makes it dangerously convenient. a.c matches "abc", "a7c", and "a c". If you literally mean a period, escape it: \..
Try it now: in the tester, run \d\d\d against "call me at 555-0192". It highlights 555 and 019. That second match surprises most people, and understanding why (regex matches wherever it can, with no idea of your intent) is the moment regex starts making sense.
Level 3: Quantifiers
Quantifiers say how many times the previous item repeats:
*means zero or more+means one or more?means zero or one, i.e. optional{3}means exactly 3,{2,5}between 2 and 5,{2,}at least 2
So \d{3}-\d{4} matches "555-0192". And colou?r matches both "color" and "colour".
Greedy vs lazy: the classic trap
Quantifiers are greedy: they grab as much as possible while still allowing a match. Given the text <b>bold</b> and <i>italic</i>, the pattern <.*> does not match <b>. It matches the entire string from the first < to the last >, because .* consumed everything in between.
Adding ? after a quantifier makes it lazy, matching as little as possible: <.*?> matches <b>, then </b>, then <i>, then </i> separately. When a pattern gobbles far more text than intended, greed is nearly always the reason.
Level 4: Anchors and Boundaries
Anchors match positions, not characters:
^matches the start of the string$matches the end\bmatches a word boundary, the position between a word character and a non-word character
Anchors turn "contains" into "is". The pattern \d+ finds digits anywhere in "abc123", but ^\d+$ requires the entire string to be digits, which is what you actually want for validating a quantity field.
And boundaries solve the "cat in scatter" problem from Level 1: \bcat\b matches "cat" as a word, but not the "cat" inside "scatter" or "catalog".
For validation work, anchor everything. An email check without ^ and $ will happily approve garbage my@email.com garbage.
Level 5: Groups and Alternation
Parentheses group things, and the pipe means "or":
(cat|dog)matches either word(ab)+matches "ab", "abab", "ababab"; the quantifier applies to the whole group^(GET|POST|PUT|DELETE)matches HTTP method lines in a log
Groups also capture what they matched, which powers replacement. Say you have dates like 07/08/2026 and need 2026-07-08. Pattern: (\d{2})/(\d{2})/(\d{4}). Each parenthesized part is stored: group 1 is 07, group 2 is 08, group 3 is 2026. The replacement string $3-$1-$2 (or \3-\1-\2 in some tools) reassembles them in the new order. This single technique, find with groups and reorder in the replacement, will save you hours of manual editing over your career. Our Find and Replace tool supports regex replacements if you want to practice on real text.
If you need grouping without capturing, (?:...) groups silently. It keeps group numbers tidy in complex patterns.
Named groups make patterns self-documenting: (?<year>\d{4}) captures into the name "year", referenced as $<year> in JavaScript replacements.
Level 6: Escaping Special Characters
These characters have special meanings and must be escaped with a backslash to be matched literally: . * + ? ^ $ ( ) [ ] { } | \ /.
To match the price "$5.99" you write \$5\.99. Forgetting to escape a dot is the most common regex bug in the wild, because an unescaped dot still works; it just also matches things it should not. buildquill.com as a pattern happily matches "buildquillXcom".
Putting It Together: Building a Pattern Step by Step
Real patterns are built incrementally, testing at each step. Suppose we need to match prices like "$1,299.99" in text.
- Start with the dollar sign, escaped:
\$ - Add the first group of digits, 1 to 3 of them:
\$\d{1,3} - Allow comma-separated thousands groups, repeated:
\$\d{1,3}(?:,\d{3})* - Allow an optional decimal part:
\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?
Test each stage against positive examples ("$5", "$1,299.99") and negative ones ("$12345.9", "$,300"). Building in steps with continuous testing is how experienced people write regex. Nobody writes the final pattern in one go.
Patterns You Will Actually Use
Here is a starter kit of practical patterns to study, not just copy. Each uses only the concepts above.
Trim whitespace from line ends (for cleanup with replace):
\s+$ replaced with nothing, in multiline mode.
Collapse multiple spaces to one:
{2,} replaced with a single space.
Extract all hashtags:
#\w+
Match a time like 09:30 or 23:59:
^([01]\d|2[0-3]):[0-5]\d$
Match an IPv4 octet-ish address (pragmatic version):
^\d{1,3}(\.\d{1,3}){3}$
Note this accepts "999.999.999.999". For most log-parsing work that is fine. The strict version exists but is much longer, and knowing when loose is acceptable is part of the skill.
A pragmatic email check:
^[^\s@]+@[^\s@]+\.[^\s@]+$
This says: some non-space non-@ characters, an @, more of them, a dot, more of them. It rejects obvious garbage and accepts all real emails. Do not chase the "perfect" email regex; even the official standard's pattern is famously monstrous, and the only true validation is sending a confirmation email.
The Flags
Every regex engine supports modifier flags. The three that matter daily:
- i (ignore case):
catwith the i flag matches "Cat" and "CAT" - g (global): find all matches rather than stopping at the first
- m (multiline): makes
^and$match at the start and end of each line rather than the whole string, which is essential when processing line-based text like logs and CSVs
How to Practice So It Sticks
Regex knowledge evaporates without use. Three exercises that build durable skill:
- Narrate patterns you meet. When you encounter a regex in code, translate it to English out loud, left to right. "Start of string, one or more digits, a dash..." If you cannot narrate it, test it against sample inputs until you can.
- Solve your own text problems with it. Next time you need to clean a list, reformat dates, or extract values from a log, resist the urge to do it by hand. The task will take longer the first five times, and then never again.
- Read failures closely. When a pattern misbehaves, do not shotgun modifications. Find the exact input where behavior diverges from your expectation, then work out which token is responsible. This is also exactly how you learn the greedy/lazy and anchoring lessons at a gut level.
A note on catastrophic backtracking: patterns with nested quantifiers like (a+)+ against non-matching input can take exponential time and freeze applications. If a pattern hangs on certain inputs, look for nested repetition and rewrite so the possibilities do not multiply.
Where to Go from Here
You now know character classes, quantifiers, anchors, groups, alternation, escaping, and flags. That covers well over 90 percent of real-world regex use. The remaining topics, lookahead (?=...), lookbehind (?<=...), backreferences \1, and Unicode classes \p{...}, are worth learning after the basics feel automatic, and each solves a specific problem you will recognize when you hit it.
Keep the Regex Tester bookmarked, build patterns incrementally, and narrate what you read. Within a few weeks of casual use, the unreadable line at the top of this article will parse in your head like a sentence.