Regular Expressions Explained
Everything a regular expression can do, in one page — the definition, the history, the full syntax reference, real examples you can run, and the mistakes worth avoiding.
Last updated
In short
\d3-\d4means "three digits, a hyphen, four digits" — and the regex engine finds every piece of text that fits. Regex is built into JavaScript, Python, Java, Go, PHP, Ruby, C#, Rust, and command-line tools like grep and sed, which makes it one of the most portable skills in software.What is a regular expression?
A regular expression is a sequence of characters where some characters stand for themselves and others are metacharacters that carry special meaning. The pattern catmatches the literal letters c-a-t. The pattern c.t matches cat,cot, cut, and any other three-letter string starting with c and ending with t, because . is a metacharacter meaning "any character".
That single idea — mixing literal text with symbols that describe categories and repetition — is the whole of regex. Everything else is vocabulary. A regex engine takes your pattern, walks through the subject text, and reports where the pattern matched, what each group captured, and how long each match was.
Three terms get used interchangeably, and all mean the same thing:
- Regular expression — the full name.
- Regex — the everyday abbreviation.
- Regexp — the abbreviation used by JavaScript's
RegExpobject and by Ruby.
Why regular expressions matter
Text is the interface between almost every system. Logs, CSV exports, form input, config files, API payloads, source code, and scraped HTML are all text, and almost every job involving them comes down to one of four questions:
- Does this text fit the shape I expect? — validating an email address, a postcode, a password policy.
- Where does this shape occur? — finding every IP address in a server log.
- What are the pieces? — pulling year, month, and day out of a timestamp.
- Can I rewrite it? — renaming a symbol across a codebase, normalising whitespace.
Regular expressions answer all four with one syntax that works in every language you are likely to use. A pattern you write in a Python script will run, essentially unchanged, in a JavaScript form validator, a Java backend, and a grep command in your terminal.
The fastest way to internalise this is to run a pattern and watch it match. Paste any of the examples below into the regex tester and edit them live.
A short history of regex
Regular expressions started as mathematics, not tooling. In 1951 the logicianStephen Cole Kleene described regular sets — the family of patterns that a finite automaton can recognise — and introduced the star operator that still bears his name. The * in ab* is the Kleene star.
In the mid-1960s Ken Thompson built Kleene's notation into the QED text editor and then into ed on Unix, compiling patterns straight to machine code for speed. The ed command for "global search for a regular expression and print" wasg/re/p/ — which is where the grep utility got its name.
Through the 1970s and 1980s regex spread across the Unix toolchain — sed,awk, vi, lex — each adding its own dialect. POSIX standardised two of them, Basic and Extended Regular Expressions, but the version that shaped modern practice arrived with Perl in 1987. Perl added lazy quantifiers, lookaround, named groups, and inline modifiers, and its syntax became so influential that the PCRE library ("Perl Compatible Regular Expressions") is now what PHP, R, and countless other tools embed.
The last major branch is RE2, released by Google in 2010. RE2 drops backreferences and lookaround in exchange for a guarantee that matching runs in time linear to the input — the trade Go and several security-sensitive systems chose deliberately. Meanwhile JavaScript's engine has kept gaining features: named groups, lookbehind, and Unicode property escapes all landed in ES2018, and the v flag with set operations arrived in ES2024.
Regular expression syntax reference
Every pattern is assembled from six categories of building block. Learn them in this order and each one builds on the last. Theprintable regex cheat sheet has the same material in a denser format for when you just need a reminder.
Anchors — where the match may start and end
Anchors match a position rather than a character. They consume nothing, which is why^abc$ is three characters wide, not five.
| Token | What it matches | Example |
|---|---|---|
^ | Start of the string, or start of a line with the m flag | ^Hello |
$ | End of the string, or end of a line with the m flag | world$ |
\b | Word boundary — the edge between a word character and a non-word character | \bcat\b |
\B | Anywhere that is not a word boundary | \Bcat |
Character classes — which characters are allowed
A character class matches exactly one character out of a set. Square brackets define your own set; the backslash shorthands cover the sets you need most often.
| Token | What it matches | Example |
|---|---|---|
. | Any character except a line break | a.c |
\d | Any digit, 0–9 | \d{4} |
\D | Any character that is not a digit | \D+ |
\w | Any letter, digit, or underscore | \w+ |
\W | Any character that is not a word character | \W |
\s | Any whitespace: space, tab, newline | \s+ |
\S | Any non-whitespace character | \S+ |
[abc] | Any one of the listed characters | gr[ae]y |
[^abc] | Any character except the listed ones | [^0-9] |
[a-z] | Any character in the given range | [A-Za-z]+ |
Quantifiers — how many times
A quantifier applies to the item immediately before it. By default quantifiers aregreedy: they take as much text as they can and then give characters back until the rest of the pattern fits. Adding ? makes them lazy, taking as little as possible instead — the difference between <.+> swallowing a whole line of HTML and <.+?> stopping at the first closing bracket.
| Token | What it matches | Example |
|---|---|---|
* | Zero or more of the preceding item | ab*c |
+ | One or more of the preceding item | \d+ |
? | Zero or one — makes the item optional | colou?r |
{n} | Exactly n repetitions | \d{3} |
{n,} | n or more repetitions | \w{8,} |
{n,m} | Between n and m repetitions | \d{2,4} |
*? +? ?? | Lazy versions — match as few characters as possible | <.+?> |
Groups and alternation — structure and choice
Parentheses do two jobs: they bundle several tokens so a quantifier can apply to all of them, and they capture the matched text so you can read it back or reuse it in a replacement.
| Token | What it matches | Example |
|---|---|---|
(...) | Capturing group — remembers the matched text for later use | (\d{4})-(\d{2}) |
(?:...) | Non-capturing group — groups without storing the result | (?:ab)+ |
(?<name>...) | Named capturing group, retrieved by name instead of index | (?<year>\d{4}) |
| | Alternation — match the expression on either side | cat|dog |
\1 | Backreference to whatever group 1 captured | (\w)\1 |
Lookaround — context without consuming it
Lookaround assertions check what sits either side of the current position without including it in the match. They are how you express "a number preceded by a dollar sign" while capturing only the number.
| Token | What it matches | Example |
|---|---|---|
(?=...) | Positive lookahead — the following text must match, but is not consumed | \d+(?= dollars) |
(?!...) | Negative lookahead — the following text must not match | ^(?!admin)\w+ |
(?<=...) | Positive lookbehind — the preceding text must match | (?<=\$)\d+ |
(?<!...) | Negative lookbehind — the preceding text must not match | (?<!\$)\d+ |
Flags — engine-wide switches
Flags change how the whole pattern is applied. In JavaScript they follow the closing slash (/pattern/gi); in Python they are passed as constants such asre.IGNORECASE.
| Token | What it matches | Example |
|---|---|---|
g | Global — find every match instead of stopping at the first | /\d+/g |
i | Case-insensitive matching | /hello/i |
m | Multiline — ^ and $ match at each line break | /^\d+$/m |
s | Dotall — . also matches newline characters | /<div>.*<\/div>/s |
u | Unicode mode — enables \p{...} property escapes and correct handling of astral characters | /\p{Lu}/u |
y | Sticky — match only at the exact lastIndex position | /\d+/y |
Worked examples
These are the patterns people actually reach for. Each one links to a full page with a breakdown, test cases, and snippets in a dozen languages.
Match a 4-digit year
\b(19|20)\d{2}\bWord boundaries stop it matching four digits inside a longer number. The group(19|20) restricts it to plausible years rather than any four digits, and\d{2} takes the remaining pair.
Extract the parts of an ISO date
^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$Named groups make the result readable: match.groups.year beatsmatch[1] when someone else reads the code six months later. See the fullISO date regex for edge cases around month and day ranges.
Validate a password policy
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{12,}$Four lookaheads each check one requirement from the same starting position without consuming anything, then .{12,} does the actual matching. This is the standard way to express "all of these conditions must hold" in a single pattern — see thestrong password regex for the full breakdown.
Find every hex colour in a stylesheet
#(?:[0-9a-fA-F]{3}){1,2}\bThe non-capturing group repeats once for #f0a and twice for #ff00aa, covering both shorthand and full form. Full write-up:hex colour regex.
Collapse repeated whitespace
\s{2,}Replace with a single space to normalise text pasted from PDFs or scraped from HTML. More atduplicate spaces regex.
The pattern library has 100+ more, each with a token-by-token explanation and a live tester.
Regular expressions across languages
The pattern is portable; the surrounding API is not. Here is the same job — find all matches — in five languages.
JavaScript
const emails = text.match(/[\w.+-]+@[\w-]+\.[\w.]+/g) ?? [];Python
import re
emails = re.findall(r"[\w.+-]+@[\w-]+\.[\w.]+", text)Java
Matcher m = Pattern.compile("[\\w.+-]+@[\\w-]+\\.[\\w.]+").matcher(text);
while (m.find()) { results.add(m.group()); }Go
re := regexp.MustCompile(`[\w.+-]+@[\w-]+\.[\w.]+`)
emails := re.FindAllString(text, -1)PHP
preg_match_all('/[\w.+-]+@[\w-]+\.[\w.]+/', $text, $matches);Two portability rules save most of the pain. First, use raw strings — Python'sr"...", Go's backticks — so you write \d instead of\\d. Second, check lookbehind support before you rely on it: JavaScript has had it since ES2018 and Python and PCRE support fixed-width lookbehind, but Go's RE2 has none at all. For language-specific detail see the Python regex guide and theJavaScript regex guide.
Common regex mistakes
Forgetting that the dot is greedy
<b>.*</b> against <b>one</b> and <b>two</b>matches the entire string, not the first tag pair. Use the lazy form<b>.*?</b>, or better, a negated class like<b>[^<]*</b> which cannot overshoot at all.
Leaving the pattern unanchored during validation
/\d{5}/ accepts "abc12345xyz" because it only needs five digitssomewhere. Validation almost always wants /^\d{5}$/.
Not escaping the dot inside a character class — and over-escaping it
Outside a class, . means "any character", so a domain check needs\.. Inside a class, [.] is already literal — escaping it there adds noise without changing behaviour.
Reusing a global regex object
In JavaScript a regex with the g or y flag carries a mutablelastIndex. Calling .test() on the same object twice returnstrue then false for identical input. Create the regex where you use it, or reset lastIndex to 0.
Parsing HTML, JSON, or code with regex
These formats nest arbitrarily deep, and no regular expression can track unbounded nesting. A pattern will handle your sample and then break on the first nested tag or escaped quote. Use a parser — DOMParser, JSON.parse, a real tokenizer.
Writing a pattern that can hang the process
Nested quantifiers over overlapping alternatives, such as (a+)+$ or(\w+\s?)*$, can push a backtracking engine into exponential work. If a pattern touches user input, keep quantifiers un-nested and anchor it so failure is immediate.
Assuming \w and \d are Unicode-aware
In JavaScript and Python 3, \d and \w behave differently depending on flags and language. If you need "any uppercase letter in any script", use the Unicode property escape \p{Lu} with the u flag rather than [A-Z].
Best practices
- Anchor validation patterns. If the whole string must match, say so with
^and$. - Prefer negated classes over lazy dots.
[^"]*is both faster and more precise than.*?when you know what must not appear. - Use non-capturing groups when you do not need the capture.
(?:...)keeps group numbering meaningful and avoids storing text you will never read. - Name your groups.
(?<area>\d{3})survives refactoring in a way thatmatch[2]does not. - Comment long patterns. Python's
re.VERBOSEand PCRE'sxflag let you break a pattern across lines with inline comments. Anything over about 40 characters earns one. - Compile once, reuse many times. In loops, hoist the pattern out — Python's
re.compile, Go'sMustCompile, a module-level constant in JavaScript. - Test the failures, not just the successes. A validation regex is only as good as the invalid input it rejects. Keep a list of both.
- Escape anything user-supplied before interpolating it into a pattern, otherwise a stray
(becomes a syntax error and a stray.*becomes a security hole. - Reach for a simpler tool when there is one.
startsWith,includes, andsplitare clearer than a regex that does the same job.
Learn regex next
If you are starting from zero, work through the28-chapter regex course in order — it moves from literals to lookbehind with a runnable demo in every chapter. If you already know the basics and want reps, theregex challenges grade your pattern against hidden test cases, and theregex quiz checks the concepts.
When you are building something specific, start from thevisual regex generator to assemble the pattern, then move it to the regex tester to check it against real data before it goes anywhere near production.
Regular Expression FAQs
What is a regular expression?
A regular expression, usually shortened to regex or regexp, is a string of characters that describes a search pattern. Instead of searching for one fixed word, you describe the shape of the text you want — for example "three digits, a hyphen, then four digits" — and the regex engine finds every piece of text with that shape. Regular expressions are built into almost every programming language and are used for validation, search-and-replace, parsing, and data extraction.
What is the difference between regex and regular expressions?
There is no difference in practice. "Regex" and "regexp" are abbreviations of "regular expression". In computer science theory, a regular expression describes a regular language, while most modern engines add features such as backreferences and lookaround that go beyond that formal definition — so what developers call regex today is more powerful than a strictly regular expression.
Is regex the same in every programming language?
The core syntax is nearly identical everywhere: anchors, character classes, quantifiers, groups, and alternation behave the same in JavaScript, Python, Java, PHP, Go, Ruby, C#, and Rust. The differences appear at the edges — lookbehind support, named-group syntax, Unicode property escapes, and how the pattern is written in code (a literal in JavaScript, a string in Python, a compiled object in Java). Go's RE2 engine deliberately omits backreferences and lookaround so that matching always runs in linear time.
How do I learn regex quickly?
Learn the five building blocks in order — literals, character classes, anchors, quantifiers, and groups — and test each one against real text as you go. That covers the large majority of patterns you will ever write. Lookaround, backreferences, and Unicode property escapes can wait until you hit a problem that needs them. Working through short exercises with immediate feedback is far more effective than reading a full syntax reference top to bottom.
When should I not use a regular expression?
Avoid regex for nested or recursive structures such as HTML, XML, JSON, and source code — those grammars are not regular, so a pattern that appears to work will fail on valid input. Use a real parser instead. Regex is also the wrong tool for full email deliverability checks (send a confirmation message), for arithmetic comparisons such as "a number between 1 and 255", and anywhere a simple string method like startsWith or split is clearer.
What does catastrophic backtracking mean?
Catastrophic backtracking happens when nested quantifiers give a backtracking engine an exponential number of ways to try to match the same text, so a short input can hang a server for seconds or minutes. A pattern like (a+)+$ against a long run of a's is the classic example. Fix it by making groups possessive or atomic where supported, by rewriting so alternatives cannot overlap, or by anchoring the pattern so it fails fast.
Keep Going
Regex Tester
Test a regular expression against sample text and see every match highlighted live.
Regex Generator
Build a pattern by clicking blocks — no syntax memorisation required.
Regex Cheat Sheet
Every token, quantifier, and assertion on one printable page.
Python Regex Guide
The re module end to end — search, match, findall, groups, and substitution.
Regex Pattern Library
100+ ready-to-use patterns for email, URLs, dates, IPs, and more.