Copy-paste regex patterns that work: email, URL, phone, dates, IPv4, semver, hex colors, slugs — with test strings, Python/JS/PCRE flavor differences, and ReDoS-safe construction rules.
./install.sh regex-recipesCopy-paste regex patterns that actually work, with test strings and the gotchas that bite you at 2am. Flavors covered: Python re, JavaScript, and PCRE (grep -P, PHP). Differences are flagged where they matter.
Golden rules before the recipes:
^...$ for "is this an email", bare pattern for "find emails in this text".^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
user.name+tag@example.comuser@, @example.com, user@example (no TLD)https?://[^\s/$.?#].[^\s]*$
https://example.com/path?q=1#frag$ anchor and use the global flag.https://example.com. often captures the period. Strip [.,;:!?)] from the end after matching: in Python, url.rstrip('.,;:!?)').^\+?[0-9][0-9\s().-]{6,18}[0-9]$
+1 (205) 555-0142, 2055550142, +44 20 7946 0958abc, 123 (too short)len(re.sub(r'\D', '', s)) between 7 and 15.^\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$ISO 8601 (strict-ish):
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
US MM/DD/YYYY:
^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{4}$
2026-02-30 passes the pattern but isn't a real date. Regex checks shape, not validity — parse with a real date library after.datetime.strptime / Date for truth.^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$
192.168.1.1, rejects 999.1.1.1(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d) is the standard 0-255 matcher — memorize it, it recurs everywhere.^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$
1.2.3, 2.0.0-beta.1+build.42^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
#fff, #ff8800, #ff8800cc (with alpha). Rejects #ffff.^[a-z0-9]+(?:-[a-z0-9]+)*$
my-cool-post-2, rejects -leading, trailing-, double--dash, UPPERCASE, under_scores.Collapse runs of whitespace to single spaces:
# Python
re.sub(r'\s+', ' ', text).strip()
# JavaScript
text.replace(/\s+/g, ' ').trim()
Split on blank lines (paragraphs):
re.split(r'\n\s*\n', text)
Integer or decimal, optional sign:
^[+-]?(?:\d+\.?\d*|\.\d+)$
Thousands-separated (US):
^\d{1,3}(?:,\d{3})*(?:\.\d+)?$
1234 (no comma). If both plain and grouped numbers are valid input, strip commas first, then use the first pattern."([^"\\]|\\.)*"
"say \"hi\"" correctly, stopping at the unescaped quote.([^"\\]|\\.) idiom — "not a quote or backslash, OR an escape sequence" — is the standard safe way to match quoted content.<[^>]+>
'' to strip tags. Works for well-formed markup.| Feature | Python re | JavaScript | PCRE | |---|---|---|---| | Named groups | (?P<name>...) | (?<name>...) | (?<name>...) | | Lookbehind | fixed-width only | fixed-width only (modern) | variable-width ok | | \d | Unicode digits by default | ASCII unless u flag | ASCII unless Unicode mode | | Dot-all | re.DOTALL | s flag | s modifier |
\d matches ٣ (Arabic-Indic digit) etc. Use [0-9] or re.ASCII when you mean ASCII digits.(?P<n>) syntax; backreference to a named group is \k<name>.Python verbose mode — comments and whitespace inside the pattern:
python
phone_re = re.compile(r"""
^\+? # optional country-code plus
[0-9] # first digit
[0-9\s().-]{6,18} # middle chunk
[0-9]$ # last digit
""", re.VERBOSE)
Named groups instead of numbered:
python
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', s)
m.group('year') # clearer than m.group(1)
Nested quantifiers like (a+)+$, (x+x+)+y can hang forever on non-matching input. Rules:
(a+)+ is the classic killer. Rewrite as (a+) or use atomic groups where supported: (?>a+)+.(?:ab|a) is safer ordered as (?:ab|a) with the longer alternative first.regex module which supports timeout=.'a' 50 + '!') and time it. If it takes >10ms, rewrite it.| Token | Meaning | |---|---| | . | any char except newline | | \d \w \s | digit / word char / whitespace | | \D \W \S | negations | | ^ $ | start / end of string (or line with m) | | \b | word boundary | | + ? | 0+, 1+, 0-or-1 (greedy) | | ? +? ?? | lazy versions | | {n} {n,} {n,m} | exact / at-least / range | | (...) | group; (?:...) non-capturing | | (?=...) / (?<=...) | lookahead / lookbehind | | a\|b | alternation | | [abc] [^abc] [a-z] | char class / negated / range | | \1 or \k<name> | backreference |
^$) if validating, unanchored if searching?- at the start/end or escaped?\d, \w checked for this flavor?publisher key 235ec8c5dc46ef241f1c11c2301c5039203667ddd06efb8f3bdbd7bedf6e37e1
No ratings yet — be the first to rate it.