Regex Syntax Quick Reference
Regular expressions use a compact syntax where most characters match themselves, but certain characters have special meaning.
Character Classes
| Pattern | Matches |
|---|---|
. |
Any character except newline |
\w |
Word character: [a-zA-Z0-9_] |
\W |
Non-word character |
\d |
Digit: [0-9] |
\D |
Non-digit |
\s |
Whitespace (space, tab, newline) |
\S |
Non-whitespace |
[abc] |
Any of a, b, or c |
[^abc] |
Anything except a, b, c |
[a-z] |
Any lowercase letter |
[A-Z] |
Any uppercase letter |
[0-9] |
Any digit (same as \d) |
[a-zA-Z] |
Any letter |
Quantifiers
| Pattern | Matches |
|---|---|
* |
0 or more |
+ |
1 or more |
? |
0 or 1 (optional) |
{n} |
Exactly n times |
{n,} |
n or more times |
{n,m} |
Between n and m times |
*? |
0 or more (lazy) |
+? |
1 or more (lazy) |
Greedy vs lazy matters when the pattern could match multiple ways. <.*> on <b>text</b> matches the entire string. <.*?> matches <b> only.
Anchors and Boundaries
| Pattern | Matches |
|---|---|
^ |
Start of string (or start of line in multiline mode) |
$ |
End of string (or end of line in multiline mode) |
\b |
Word boundary |
\B |
Not a word boundary |
\A |
Start of string (Python, ignores multiline) |
\Z |
End of string (Python, ignores multiline) |
Groups and Alternation
| Pattern | Meaning |
|---|---|
(abc) |
Capturing group |
(?:abc) |
Non-capturing group |
(?<name>abc) |
Named capturing group |
| `a | b` |
(?=abc) |
Positive lookahead: followed by abc |
(?!abc) |
Negative lookahead: not followed by abc |
(?<=abc) |
Positive lookbehind: preceded by abc |
(?<!abc) |
Negative lookbehind: not preceded by abc |
Real-World Patterns
Email Address
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
This matches the vast majority of valid email formats. Full RFC 5322 compliance requires a much more complex regex — but this 95% solution handles all common cases.
Matches: user@example.com, first.last+tag@sub.domain.co.uk
Rejects: user@, @example.com, user @example.com
URL
https?://[^\s/$.?#].[^\s]*
Matches http and https URLs. For production validation, use the URL constructor instead of regex — it handles all edge cases correctly.
IPv4 Address
^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)){3}$
Validates that each octet is 0–255. Matches: 192.168.1.1, 0.0.0.0, 255.255.255.255. Rejects: 256.0.0.1, 192.168.1.
Phone Number (US)
^(\+1[\s.-]?)?\(?[2-9]\d{2}\)?[\s.-]?[2-9]\d{2}[\s.-]?\d{4}$
Matches: 555-867-5309, (555) 867-5309, +1 555 867 5309
Date (YYYY-MM-DD)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Validates format and range (months 01–12, days 01–31). Does not validate day/month combinations — use a date library for that.
Time (HH:MM:SS)
^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$
24-hour format. Hours 00–23, minutes and seconds 00–59.
Strong Password
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$
Requires at least 8 characters, one uppercase, one lowercase, one digit, one special character. Uses lookaheads to check each requirement independently.
Hex Color Code
^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
Matches #ff6600 and shorthand #f60. Case insensitive.
Slug (URL-friendly string)
^[a-z0-9]+(?:-[a-z0-9]+)*$
Matches: my-blog-post, product-123. Rejects: -starts-with-dash, has spaces, UPPERCASE.
Credit Card Number
^(4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12})$
Matches Visa, MasterCard, Amex, Discover. Always strip spaces/dashes before matching.
JWT Token
^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$
Three Base64url-encoded segments separated by dots.
HTML Tag
<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>(.*?)<\/\1>
Matches a tag and its content. Note: parsing HTML with regex is fragile for nested elements — use a proper HTML parser for anything complex.
Markdown Header
^(#{1,6})\s+(.+)$
Captures heading level and text. Group 1: # to ######. Group 2: heading text.
Code Examples
JavaScript
// Test if a string matches
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
emailRegex.test('user@example.com'); // true
// Extract matches
const text = 'Call 555-867-5309 or 555-123-4567';
const phoneRegex = /\d{3}-\d{3}-\d{4}/g;
text.match(phoneRegex); // ['555-867-5309', '555-123-4567']
// Replace matches
'hello_world_test'.replace(/_/g, '-'); // 'hello-world-test'
// Named capture groups
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { year, month, day } = '2026-05-15'.match(dateRegex).groups;
Python
import re
# Test match
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}#39;
bool(re.match(pattern, 'user@example.com')) # True
# Find all matches
text = 'Dates: 2026-01-15 and 2026-03-20'
dates = re.findall(r'\d{4}-\d{2}-\d{2}', text)
# ['2026-01-15', '2026-03-20']
# Named groups
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2026-05')
m.group('year') # '2026'
# Replace with function
result = re.sub(r'\b(\w)', lambda m: m.group().upper(), 'hello world')
# 'Hello World'
Flags Reference
| Flag | JavaScript | Python | Effect |
|---|---|---|---|
| Case insensitive | /regex/i |
re.IGNORECASE |
A matches a |
| Multiline | /regex/m |
re.MULTILINE |
^ and $ match line boundaries |
| Dotall | /regex/s |
re.DOTALL |
. matches newlines |
| Global | /regex/g |
(use findall) |
Find all matches, not just first |
| Verbose | N/A | re.VERBOSE |
Allows whitespace and comments in pattern |
Performance Tips
Compile patterns you reuse — In Python, re.compile(pattern) creates a reusable regex object. In JavaScript, a regex literal /pattern/ is compiled once per load.
Avoid catastrophic backtracking — Patterns like (a+)+ on long strings can take exponential time. This is the source of ReDoS (Regular Expression Denial of Service) attacks. Keep quantifiers simple and avoid nested repetition on the same character class.
Use non-capturing groups when you don't need the match — (?:abc) is faster than (abc) because the engine doesn't store the match.
Anchor when possible — Adding ^ and $ where appropriate stops the engine from searching the entire input when the match fails.
FAQ
What's the difference between match and search in Python?
re.match only matches at the beginning of the string. re.search scans through the string looking for any match. For most use cases, re.search is what you want.
Why does my regex work in one language but not another?
Different regex engines support different features. Lookaheads/lookbehinds, named groups, and Unicode properties vary by engine. JavaScript lacks lookbehind in older environments; Python's re module has full lookbehind support.
How do I match a literal dot, asterisk, or other special character?
Escape it with a backslash: \. matches a literal dot; \* matches a literal asterisk.
→ Use the Regex Tester to write, test, and debug regex patterns interactively — with real-time match highlighting and flag toggles.