Regular Expressions for Beginners
Regular expressions look like someone fell asleep on the keyboard: ^\d{3}-\d{4}$. They also happen to be one of the highest-leverage skills a developer can learn — a compact language for describing patterns in text, understood by virtually every programming language, text editor and command-line tool. This guide builds regex up from nothing, one idea at a time.
What a regex actually is
A regular expression is a patternthat either matches a piece of text or does not. “Find every email address,” “check this looks like a phone number,” “replace every double space with a single one” — all regex jobs.
The simplest pattern is just literal text: cat matches the letters c-a-t anywhere it finds them. The power comes from special charactersthat mean “any digit” or “one or more of these” rather than a literal.
Character classes: matching types of thing
These are the workhorses. Instead of a specific character, they match a category:
\d— any digit (0–9)\w— any “word” character (letters, digits, underscore)\s— any whitespace (space, tab, newline).— any character at all
Capitalising a class inverts it: \D is any non-digit, \S any non-whitespace. And you can build your own class with square brackets: [aeiou] matches any vowel, [a-z] any lowercase letter, [^0-9] anything that is not a digit (a ^inside brackets means “not”).
Quantifiers: how many
A character class matches one character. Quantifiers say how many to expect:
*— zero or more+— one or more?— zero or one (optional){3}— exactly three{2,5}— between two and five{2,}— two or more
So \d+ matches one or more digits — a whole number. \d{4} matches exactly four digits, like a year. And colou?r matches both color and colour, because the u is optional.
Anchors: where in the text
By default a pattern matches anywhere. Anchors pin it to a position:
^— the start of the text (or line)$— the end\b— a word boundary
The difference matters enormously. \d{5} finds five digits anywhere — it would happily match part of a longer number. ^\d{5}$ matches only if the entirestring is exactly five digits, which is how you validate a US ZIP code. Anchors are the difference between “contains” and “is.”
Capture groups: extracting the pieces
Parentheses do two jobs. They group things together so a quantifier applies to the whole group — and they capture what matched, so you can pull it out.
Take a date pattern: (\d{4})-(\d{2})-(\d{2}). Against 2026-07-13 it matches the whole date, and also captures 2026, 07 and 13 as groups 1, 2 and 3 — ready to be reused or rearranged. This is how you reformat data: capture the pieces, then put them back in a different order.
Greedy vs. lazy: the classic trap
This is the single most confusing thing for beginners, and worth understanding early.
By default, quantifiers are greedy — they match as much as possible. Suppose you want to extract the tag from <b>hello</b> using <.+>. You expect <b>. Instead you get the entire string <b>hello</b>, because .+ greedily grabbed everything up to the last >.
Add a ? after the quantifier to make it lazy — matching as little as possible. <.+?> stops at the first > and correctly returns <b>. If a pattern is matching far more than you expected, greediness is almost always the reason.
Flags
Flags change how the whole pattern behaves:
g— global: find all matches, not just the first.i— case-insensitive.m— multiline:^and$match at each line break.s— let.match newlines too.
The mistakes every beginner makes
- Forgetting to escape the dot.
example.commatchesexampleXcomtoo, because.means “any character.” For a literal dot, write\.. - Greedy matching grabbing far too much. Reach for lazy
+?or a more specific pattern. - Trying to parse HTML with regex. A famous rule of thumb: do not. HTML is nested and irregular in ways regex cannot handle. Use a proper parser.
- Not anchoring a validation. Validating a ZIP code with
\d{5}passesabc12345xyz. Anchor it:^\d{5}$. - Over-engineering.The “perfect” email regex is thousands of characters long and still imperfect. A simple pattern that catches obvious typos is usually the right tool.
A practical example, built up
Say you want to match a simple email. Reason through it:
\w+— one or more word characters (the name)@— a literal at-sign\w+— the domain name\.— a literal dot (escaped)\w+— the extension
Put together: \w+@\w+\.\w+. Not bulletproof, but it catches most real addresses and all obvious mistakes — which is usually exactly what you need.
Frequently asked questions
Are regex the same in every language? The core syntax is nearly universal. Advanced features (lookbehind, named groups) vary. JavaScript, Python and PCRE agree on the basics.
Why is my regex matching too much? Greedy quantifiers. Make them lazy with ?, or be more specific.
How do I match a literal special character? Escape it with a backslash: \., \?, \(.
Should I validate emails with regex? Only loosely. A simple pattern catches typos; the only true test of an email address is sending to it.
Test your regex now
Use our Regex Tester to build and debug patterns with live match highlighting and a capture-group breakdown — nothing is uploaded, so it is safe with real data. To clean up the text you are matching against, try the Text Diff Checker.