Bookmarked and referenced dozens of times per project — this is the regular expressions reference that covers everything from the basics to the patterns you reach for in real code.
Character classes
Match a set or type of character.
| Pattern | What it matches |
|---|---|
[abc] |
Any one of: a, b, or c |
[a-z] |
Any lowercase letter |
[A-Z] |
Any uppercase letter |
[0-9] |
Any digit |
[a-zA-Z0-9] |
Any letter or digit |
[^abc] |
Any character except a, b, c (^ inside [] negates) |
. |
Any character except newline |
\d |
Digit — equivalent to [0-9] |
\D |
Non-digit — equivalent to [^0-9] |
\w |
Word character — [a-zA-Z0-9_] |
\W |
Non-word character — [^\w] |
\s |
Whitespace (space, tab, newline, carriage return) |
\S |
Non-whitespace |
Quantifiers
Control how many times the preceding element repeats.
| Pattern | Meaning |
|---|---|
* |
0 or more times |
+ |
1 or more times |
? |
0 or 1 time (makes preceding element optional) |
{n} |
Exactly n times |
{n,} |
n or more times |
{n,m} |
Between n and m times (inclusive) |
Greedy vs lazy: By default quantifiers are greedy — they match as much as possible. Add ? after any quantifier to make it lazy (match as little as possible).
"<b>bold</b> and <i>italic</i>"
Greedy: <.+> → matches entire string from first < to last >
Lazy: <.+?> → matches each tag individually: <b>, </b>, <i>, </i>
Anchors
Assert position without consuming characters.
| Pattern | Matches at |
|---|---|
^ |
Start of string (or start of line in multiline mode) |
$ |
End of string (or end of line in multiline mode) |
\b |
Word boundary (between \w and \W) |
\B |
Non-word boundary |
\A |
Absolute start of string (Python, Java, Ruby — not JS) |
\Z |
Absolute end of string (Python, Java, Ruby) |
^\d{4}$ matches a string that is exactly 4 digits
\bword\b matches "word" but not "password" or "wording"
Groups and alternation
| Pattern | Meaning |
|---|---|
(abc) |
Capturing group — matches and captures "abc" |
(?:abc) |
Non-capturing group — matches but doesn't capture |
(?<name>abc) |
Named capturing group — capture with a label |
a|b |
Alternation — matches a or b |
\1 |
Backreference to group 1 (in pattern or replacement) |
$1 or \1 |
Backreference in replacement string |
// Reorder "last, first" → "first last"
"Smith, John".replace(/(\w+), (\w+)/, "$2 $1")
// → "John Smith"
// Named group
const m = "2026-07-13".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
console.log(m.groups.year) // "2026"
Lookahead and lookbehind
Zero-width assertions — test what comes before or after the current position without including it in the match.
| Pattern | Type | Meaning |
|---|---|---|
(?=...) |
Positive lookahead | Followed by ... |
(?!...) |
Negative lookahead | NOT followed by ... |
(?<=...) |
Positive lookbehind | Preceded by ... |
(?<!...) |
Negative lookbehind | NOT preceded by ... |
\d+(?= dollars) matches digits only when followed by " dollars"
\d+(?! dollars) matches digits NOT followed by " dollars"
(?<=\$)\d+ matches digits only when preceded by "$"
(?<!\$)\d+ matches digits NOT preceded by "$"
Lookbehind support is broad in modern runtimes (Node 8+, Python, Java, .NET, PHP PCRE) but was added to V8 (Chrome/Node) later — check your target environment for older browser support.
Flags / modifiers
Applied after the closing / in literal regex syntax.
| Flag | JS | Python | Go | PHP | Effect |
|---|---|---|---|---|---|
| Case-insensitive | i |
re.I |
(?i) |
i |
A matches a |
| Multiline | m |
re.M |
(?m) |
m |
^/$ match line boundaries |
| Dotall | s |
re.S |
(?s) |
s |
. matches newline too |
| Global (all matches) | g |
(use findall) |
(use FindAll) |
g |
Find every match, not just first |
| Unicode | u |
default | default | u |
Full Unicode property support |
| Verbose (extended) | — | re.X |
(?x) |
x |
Allows whitespace and comments in pattern |
// JS: case-insensitive, global
const matches = "Hello WORLD world".match(/world/gi)
// → ["WORLD", "world"]
Escape sequences
| Sequence | Matches |
|---|---|
\. |
Literal dot (without escape, . means any char) |
\* |
Literal asterisk |
\+ |
Literal plus |
\? |
Literal question mark |
\( \) |
Literal parentheses |
\[ \] |
Literal square brackets |
\{ \} |
Literal curly braces |
\\ |
Literal backslash |
| |
Literal pipe |
\^ |
Literal caret |
\$ |
Literal dollar sign |
\t |
Tab |
\n |
Newline |
\r |
Carriage return |
Rule: Any regex special character (\ ^ $ . | ? * + ( ) [ ] { }) must be escaped with \ to match it literally.
Quick reference: language syntax
| Task | JavaScript | Python | Go | PHP |
|---|---|---|---|---|
| Test if match | /pat/.test(str) |
re.search(pat, str) |
regexp.MatchString(pat, str) |
preg_match($pat, $str) |
| Get first match | str.match(/pat/) |
re.search(pat, str).group() |
re.FindString(str) |
preg_match($pat, $str, $m) |
| Get all matches | str.match(/pat/g) |
re.findall(pat, str) |
re.FindAllString(str, -1) |
preg_match_all($pat, $str, $m) |
| Replace first | str.replace(/pat/, rep) |
re.sub(pat, rep, str, count=1) |
re.ReplaceAllString(str, rep) |
preg_replace($pat, $rep, $str, 1) |
| Replace all | str.replace(/pat/g, rep) |
re.sub(pat, rep, str) |
re.ReplaceAllString(str, rep) |
preg_replace($pat, $rep, $str) |
| Split | str.split(/pat/) |
re.split(pat, str) |
re.Split(str, -1) |
preg_split($pat, $str) |
| Compile/cache | new RegExp(pat, flags) |
re.compile(pat) |
regexp.MustCompile(pat) |
preg_match (auto-cached) |
Ready-to-use patterns
Copy and adapt these validated patterns.
Email address (simple, practical)
^[\w.+-]+@[\w-]+\.[\w.]{2,}$
Matches user@example.com, user+tag@sub.domain.co. Not RFC 5321 complete — use a proper library for strict validation.
URL (http/https)
https?://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?
IPv4 address
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$
Matches 192.168.1.1, rejects 999.0.0.1.
Date: YYYY-MM-DD (ISO 8601)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Structure-valid only — accepts 2026-02-31 (February 31). Use date parsing to validate calendar correctness.
Date: MM/DD/YYYY
^(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/\d{4}$
Time: HH:MM or HH:MM:SS (24-hour)
^([01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$
Phone number (flexible, international)
^\+?[\d\s\-().]{7,15}$
For strict E.164: ^\+[1-9]\d{7,14}$.
Postal / ZIP code (US)
^\d{5}(-\d{4})?$
UUID (v1–v5)
^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
Hex color code
^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
Matches #fff, #1a2b3c, #ff000080.
Slug (URL-friendly)
^[a-z0-9]+(?:-[a-z0-9]+)*$
Matches my-blog-post, rejects My Blog Post, --double-dash.
Credit card number (digits only, major networks)
^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|3[47]\d{13}|6011\d{12})$
Visa, Mastercard, Amex, Discover — without spaces/dashes. Never store raw card numbers; use a payment processor.
Strong password (8+ chars, upper, lower, digit, special)
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$
Whitespace-only line (for cleanup)
^\s*$
HTML tag (simple strip — not for full HTML parsing)
<[^>]+>
6 common regex mistakes
1. Forgetting to escape special characters. The pattern 3.14 matches "3x14" because . means any character. Use 3\.14 to match a literal dot.
2. Using greedy quantifiers when lazy is needed. <.+> on <b>bold</b> matches the entire string, not each tag. Use <.+?> or <[^>]+> instead.
3. Not anchoring validation patterns. The pattern \d{4} matches any string containing 4 digits — including "abc1234xyz". Use ^\d{4}$ to match strings that are only 4 digits.
4. Catastrophic backtracking. Patterns like (a+)+ on a long non-matching string can run for seconds or minutes. Keep alternation outside repeated groups, avoid nested quantifiers on the same character class.
5. Mixing up match() return value in JavaScript. Without the g flag, str.match(/pat/) returns an array where index 0 is the full match and index 1+ are capture groups. With g, it returns all full matches but drops capture groups. Use str.matchAll(/pat/g) for all matches with capture groups.
6. Using regex for structured formats. Regex cannot validate balanced brackets, matching HTML tags, or correct calendar dates. Use a parser or date library for those cases; regex is for pattern shape, not semantic correctness.
6 FAQ
Can I use regex to parse HTML? For simple extraction of known, predictable patterns — yes. For parsing arbitrary HTML, use a DOM parser (DOMParser in JS, BeautifulSoup in Python, html.parser in Go/PHP). HTML is not a regular language and nested tags break naive regex.
What's the difference between match() and search() in Python?
re.match() only matches at the start of the string. re.search() scans the entire string. Almost always use re.search() unless you specifically need start-of-string matching.
How do I match a newline with .?
By default . does not match \n. Enable dotall mode: re.S in Python, (?s) inline flag in Go/PHP, the s flag in JavaScript (/pat/s). Alternatively, use [\s\S] which matches any character including newlines and works everywhere.
What is re.compile() for in Python?
It pre-compiles the pattern into a regex object, which is slightly faster if you use the same pattern many times in a loop. For one-off matches, bare re.search(pattern, string) is fine.
How do I match Unicode characters?
In JavaScript, add the u flag: /\p{L}+/u matches any Unicode letter. Python's re module handles Unicode in strings by default; use \p{...} via the regex library for full Unicode property support. In Go, regexp natively handles UTF-8.
How can I test my regex interactively? Use a free online regex tester to write your pattern, see matches highlighted in real time, and inspect capture groups — no setup required.