Regular Expressions: A Beginner's Guide

Updated July 27, 2026

Regular expressions have a reputation for looking like line noise, but the vast majority of real-world regex use leans on a handful of building blocks. Once those click, most patterns you’ll actually need to write — or read — stop being intimidating.

Literal characters match themselves

The simplest regex, cat, matches the literal text “cat” wherever it appears. Most characters in a pattern are literal; the “special” characters below are the exceptions worth learning deliberately.

Character classes: matching a set of options

[abc] matches any one of a, b, or c. Ranges work inside brackets too: [a-z] matches any lowercase letter, [0-9] any digit. A ^ at the start negates the set — [^0-9] matches anything that isn’t a digit.

A few classes are common enough to have shorthand:

Shorthand Matches Equivalent
\d Any digit [0-9]
\w Letter, digit, or underscore [A-Za-z0-9_]
\s Any whitespace space, tab, newline
. Any character except newline

Capitalising each (\D, \W, \S) negates it — “anything except this.”

Quantifiers: how many times

By default a pattern matches exactly once. Quantifiers change that:

So \d+ matches one or more digits in a row — a whole number, roughly.

Greedy vs lazy: the classic beginner trap

Quantifiers are greedy by default: they match as much as possible, then give back only what’s needed for the rest of the pattern to succeed.

Run <.+> against <b>bold</b> and you might expect it to match <b>. It matches the entire string — .+ swallows everything, then backtracks just enough to find a final >.

Adding ? after a quantifier makes it lazy, matching as little as possible: <.+?> correctly matches just <b>.

This one behaviour accounts for a large share of “why isn’t my regex working?” moments. When a pattern matches far more than intended, greediness is the first thing to check.

Groups and capturing

Parentheses (...) group part of a pattern together — useful with quantifiers ((ab)+ matches “ab”, “abab”, …) and, separately, to capture the matched text for later use. (\d{4})-(\d{2})-(\d{2}) matching a date captures year, month, and day as three groups you can reference afterward, commonly $1, $2, $3 in a replacement.

Named groups make this readable: (?<year>\d{4})-(?<month>\d{2}) lets you refer to year and month by name instead of counting positions — worth the extra characters on anything you’ll revisit.

If you want grouping without capturing, (?:...) groups without allocating a numbered slot.

Anchors: matching a position, not a character

^ matches the start of the text (or line, with the multiline flag); $ matches the end. ^\d+$ means “the entire string is digits,” not just that it contains some — a common source of confusion for beginners expecting \d+ alone to validate a whole input.

\b matches a word boundary. \bcat\b matches “cat” in “the cat sat” but not inside “concatenate” — the fix for find-and-replace operations that mangle words containing your search term.

Escaping

Special characters need a backslash to match literally. To match an actual dot, write \. — a bare . matches any character. The characters needing escapes outside a class are . ^ $ * + ? ( ) [ ] { } | \ /.

This is why a naive domain pattern like example.com also matches exampleXcom. Write example\.com.

A worked example

^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$ is a simplified email check: one or more word/dot/plus/hyphen characters, an @, a domain, a dot, then a 2+ letter TLD — anchored so the whole string must match.

Note “simplified.” The genuinely RFC-compliant email regex is thousands of characters long and still doesn’t tell you whether the address exists. For validating email in practice, a loose check that the string contains an @ with something either side, followed by sending a confirmation message, is more reliable than any pattern. Regex is good at structure and bad at semantics.

Flags

Flags change how the whole pattern behaves:

Reaching for [A-Za-z] when you meant the i flag is a common bit of over-engineering. For the complete syntax and every flag, the MDN regular expressions guide is the reference worth keeping open.

When not to use regex

Regex matches regular languages. HTML, XML, and JSON are not regular — they nest arbitrarily, and no regex can track nesting depth correctly. A pattern that appears to parse HTML works on your three test cases and fails on real input, usually silently.

Use a real parser for structured formats. Regex is the right tool for finding patterns within a known-simple string: validating a postcode’s shape, pulling timestamps out of log lines, renaming across a codebase.

One more caution: patterns with nested quantifiers like (a+)+b can take exponential time on certain inputs, a denial-of-service vector known as ReDoS. If a pattern will ever run against user-supplied text, keep it simple and test it against hostile input, not just valid input.

Trying patterns yourself

Regex Tester runs a pattern against sample text live as you type, showing every match, its position, and any capture groups — by far the fastest way to build intuition, because you see greediness and backtracking behave rather than reasoning about them on paper.

Once a pattern does what you want, Find and Replace supports the same syntax for actually transforming text, including $1-style group references in the replacement. And if you’re writing patterns against HTML source, HTML Entity Encoder is useful for seeing what the text you’re matching against actually contains — &amp; and & are different strings to a regex, which is a subtle and frustrating source of non-matches.

Share