Regexdrill / Cheat sheet
Regex cheat sheet
Every token you will meet, then the working patterns for the jobs that come up (validation, extraction, cleanup, log parsing) in the JavaScript, Python and PCRE flavors with the differences marked. Most entries have a drill that tests your pattern on real strings. Print the page or get the PDF by email.
The ones worth learning first
| What | How |
|---|
| Any digit, any word character, any space | \d \w \s (capitals negate: \D \W \S) |
| One or more, zero or more, optional | + * ? |
| Exactly n, between n and m | {3} {2,5} |
| Start and end of the string | ^...$ |
| A set, a range, not in the set | [aeiou] [a-z0-9] [^0-9] |
| Either | cat|dog (group it: (cat|dog)s?) |
| Capture a part | (\d{4})-(\d{2}) $1 or \1 to reuse |
| Lazy instead of greedy | .*? .+? |
| Followed by / not followed by | (?=...) (?!...) |
| Ignore case, all matches | /pattern/gi re.I (?i) |
Characters
| Literal text | cat drill |
| Any character except newlineWith the s flag (dotall) it also matches newlines. | . drill |
| Escape a special characterSpecials: . * + ? ^ $ ( ) [ ] { } | \ / | \. \? \( \[ \\ \$ drill |
| Digit | \d (same as [0-9]) |
| Word character | \w (letters, digits, underscore) |
| Whitespace | \s (space, tab, newline) |
| Not a digit / word / space | \D \W \S |
| Tab, newline | \t \n \r |
| Unicode letter | \p{L} (JavaScript needs the u flag) drill |
Classes
| One of a set | [aeiou] drill |
| Range | [a-z] [A-Z0-9] [a-zA-Z] drill |
| Not in the set | [^0-9] [^\s] drill |
| Literal hyphen or bracket inside a classPut the hyphen first or last. | [-a-z] [a-z\]] |
| Hex digit | [0-9a-fA-F] drill |
| Common pairs | [\w.-] for emails and slugs |
Quantifiers
| Zero or more | a* drill |
| One or more | a+ drill |
| Optional | colou?r drill |
| Exactly n | \d{4} |
| At least n | \d{2,} |
| Between n and m | \d{2,5} |
| Greedy by default | <.+> matches the whole <a><b> |
| Lazy | <.+?> matches <a> then <b> drill |
| Possessive (PCRE, Java) | a++ (no backtracking) |
Anchors and boundaries
| Start, end of string | ^abc$ drill |
| Per line | ^...$ with the m flag drill |
| Word boundary | \bcat\b matches cat, not concat drill |
| Not a boundary | \B |
| Absolute start and end (Python, PCRE) | \A \Z |
Groups and alternation
| Either | cat|dog drill |
| Either, anchoredWithout the group, ^cat|dog$ means (^cat)|(dog$). | ^(cat|dog)$ drill |
| Capture group | (\d{4})-(\d{2})-(\d{2}) drill |
| Non-capturing group | (?:https?|ftp):// drill |
| Named group | (?<year>\d{4}) Python: (?P<year>\d{4}) drill |
| Backreference | (\w)\1 matches a doubled letter drill |
| Matching quotes | (["'])(.*?)\1 drill |
| Optional group | (\+1 )?\d{3}-\d{4} drill |
| Repeated group | (\d{1,3}\.){3}\d{1,3} drill |
| Replacement references | JavaScript: $1 $<name> Python: \1 \g<name> |
Lookarounds
| Followed by | \d+(?= dollars) drill |
| Not followed by | \d+(?! dollars) drill |
| Preceded by | (?<=\$)\d+ drill |
| Not preceded by | (?<!\$)\b\d+ drill |
| Password rules (several lookaheads) | ^(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$ drill |
| Thousands separators | \B(?=(\d{3})+(?!\d)) replace with , |
Flags
| Ignore case | /x/i re.IGNORECASE (?i)x |
| All matches | /x/g Python: re.findall / re.sub (always all) |
| Multiline anchors | /x/m re.MULTILINE |
| Dot matches newline | /x/s re.DOTALL |
| Unicode | /x/u (Python 3 strings are Unicode by default) |
| Verbose (comments and spaces) | re.VERBOSE (?x) |
Validation patterns
| Email (practical)Full RFC validation is not worth it; send a confirmation email. | ^[\w.+-]+@[\w-]+(\.[\w-]+)+$ drill |
| US ZIP | ^\d{5}(-\d{4})?$ drill |
| Phone (flexible) | ^\+?\d{1,3}?[-. ]?\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4}$ drill |
| ISO date | ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ drill |
| 24-hour time | ^([01]\d|2[0-3]):[0-5]\d$ drill |
| 12-hour time | ^(1[0-2]|0?[1-9]):[0-5]\d ?[AaPp][Mm]$ drill |
| IPv4 | ^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$ drill |
| URL slug | ^[a-z0-9]+(-[a-z0-9]+)*$ drill |
| SKU like AB-1234 | ^[A-Z]{2}-\d{4}$ drill |
| Money | ^\$?\d{1,3}(,\d{3})*(\.\d{2})?$ drill |
| Hex color | ^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$ |
| UUID | ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ |
Extraction and cleanup
| Numbers from text | \d+(\.\d+)? drill |
| Dates in text | \b\d{4}-\d{2}-\d{2}\b drill |
| Domain from an email or URL | @([\w.-]+) or https?://([^/]+) drill |
| Hashtags | #\w+ drill |
| Key=value pairs | (\w+)=("[^"]*"|\S+) drill |
| Log line | ^(\S+) (\S+) \[([^\]]+)\] "(\w+) ([^"]+)" (\d{3}) drill |
| CSV field (quoted or not) | "([^"]*)"|([^,]+) drill |
| Trim both ends | ^\s+|\s+$ replace with nothing drill |
| Collapse spaces | \s{2,} replace with one space drill |
| Strip HTML tagsFine for cleanup, not for parsing HTML. | <[^>]+> drill |
| Capitalized names | \b[A-Z][a-z]+ [A-Z][a-z]+\b drill |
| Units after a number | (\d+(?:\.\d+)?)\s?(kg|g|lb|oz)\b drill |
| Titles | \b(Mr|Mrs|Ms|Dr)\.?\s drill |
| Keep only digits | replace [^0-9] with nothing |
In code
| JavaScript test | /^\d+$/.test(s) |
| JavaScript all matches | [...s.matchAll(/(\w+)=(\d+)/g)] |
| JavaScript replace with groups | s.replace(/(\d{4})-(\d{2})/g, '$2/$1') |
| Python match at start | re.match(r'^\d+', s) |
| Python search anywhere | re.search(r'\d+', s) |
| Python all | re.findall(r'\d+', s) re.finditer(...) |
| Python replace | re.sub(r'\s+', ' ', s) |
| Python compile once | pat = re.compile(r'...'); pat.search(s) |
| pandas | df['col'].str.extract(r'(\d+)') .str.contains(r'^A', regex=True) |
| SQL | PostgreSQL: col ~ '^\d+$' MySQL: col REGEXP '^[0-9]+$' |
| Raw strings | Python r'...' and JavaScript String.raw avoid double escaping. |
regexdrill.com: practice every one of these with instant checking. 88 entries.
Questions
Why does my pattern match too much?
Quantifiers are greedy: .* takes everything it can and then backs off. Use a lazy quantifier (.*?) or, better, a negated class that cannot cross the boundary you care about, such as [^"]* inside quotes.
Why does ^cat|dog$ match 'hotdog'?
Alternation has the lowest precedence, so the pattern reads as (^cat) or (dog$). Group the alternatives: ^(cat|dog)$.
Should I validate emails with regex?
Only loosely. A practical pattern catches typos; the only real validation is sending a message to the address.
JavaScript, Python or PCRE: which flavor is this?
The tokens are common to all three. Differences that matter are marked: named groups ((?<n>) vs (?P<n>)), replacement references ($1 vs \1), the u flag for Unicode classes in JavaScript, and \A \Z which JavaScript does not have.
How do I match across lines?
Use the s (dotall) flag so . matches newlines, or [\s\S] in place of the dot. For per-line anchors use the m flag.
A cheat sheet does not make them stick
Doing them with instant checking does. The free exercises need no account.
Start practicing