Cheat Sheet Library

Regex Cheat Sheet

Patterns for finding, checking and replacing text: syntax, flags, ready-made patterns (email, URL, dates, Belgian phone/IBAN…) and how regex works in JavaScript, Python, PHP, C#, grep, sed and VS Code. Test your patterns at regex101.com.

  • 141 entries
  • 16 sections
  • Updated

141 entries

On this page

Characters & Classes #

PatternMatches
abcThe literal text "abc"
.Any character except a newline (with the s flag: also newlines)
\d / \DA digit / not a digit
\w / \WA word character (letter, digit, _) / not one
\s / \SWhitespace (space, tab, newline) / not whitespace
[abc]One of a, b or c
[^abc]Any character except a, b, c
[a-z] / [A-Za-z0-9]A range / several ranges
[-.] / [a\-z]A literal -: put it first or last, or escape it
\. \* \+ \? \( \[ \\A literal special character: escape it with \
\t \n \rTab, newline, carriage return
\p{L} / \p{Lu} / \p{N}Any letter / uppercase letter / number in any language (JS needs the u flag; not in Python's re)

In JavaScript (without u) \w and \d are ASCII only: \w doesn't match é. In Python 3 they match any Unicode letter or digit; add re.ASCII to limit them.

Anchors & Boundaries #

PatternMatches
^Start of the text (start of every line with the m flag)
$End of the text (end of every line with m)
\bWord boundary: \bcat\b finds "cat" but not "concat"
\BNot a word boundary: \Bcat finds the "cat" in "concat"
\A / \zVery start / very end of the text, ignores m (Python: \A / \Z; not in JavaScript)

For validation always anchor both ends: ^\d+$. Without anchors \d+ also "matches" abc123xyz.

Quantifiers #

PatternMeaning
a*0 or more
a+1 or more
a?0 or 1 (optional)
a{3}Exactly 3
a{2,}2 or more
a{2,5}2 to 5
*? / +? / ?? / {2,5}?Lazy: as few as possible
a*+ / (?>a*)Possessive / atomic: never gives characters back (PHP, Java, .NET, Python 3.11+; not JavaScript)

Greedy vs lazy: on <a><b>, <.+> matches everything, <.+?> matches just <a>.

Groups & Alternation #

PatternMeaning
(abc)Capturing group: group 1, 2… in the result, $1 / \1 in replacements
(?:abc)Group without capturing (for | or quantifiers only)
(?<year>\d{4})Named group (JS, PHP, .NET; Python writes (?P<year>...))
cat|dogcat or dog
(cat|dog)s?cat, cats, dog, dogs
(\w)\1Back-reference: the same text as group 1 again (ll in "hello")
\k<year>Back-reference to a named group (Python: (?P=year))

Lookarounds #

Check what comes before or after without including it in the match.

PatternMeaning
x(?=y)x, only if followed by y
x(?!y)x, only if not followed by y
(?<=y)xx, only if preceded by y
(?<!y)xx, only if not preceded by y
\d+(?=€)The number in "25€", without the €
(?<=€)\d+The number in "€25", without the €
^(?=.*\d)(?=.*[A-Z]).{8,}$Several conditions at once (here: a digit and a capital, 8+ characters)

Python's re and PHP need fixed-width lookbehind: (?<=\d+) fails, (?<=\d{2}) works. JavaScript and .NET accept any width.

Flags #

FlagEffectJavaScriptPythonPHPC#
Case-insensitivea also matches A/x/ire.I/x/iRegexOptions.IgnoreCase
Multiline^ $ match at every linemre.MmMultiline
Dot-all. also matches newlinessre.SsSingleline
GlobalFind all matches, not just the firstgfindall / finditerpreg_match_allMatches()
UnicodeFull Unicode, \p{...}u (or v, ES2024)defaultudefault
VerboseAllow spaces and # comments in the patternnot availablere.XxIgnorePatternWhitespace
InlineFlag inside the pattern: (?i)abc, (?i:ab)c(?i:...) only (ES2025, recent browsers)(?i)(?i)(?i)

Replacing #

SyntaxInsertsWhere
$1 / $2Group 1 / 2JavaScript, PHP, C#, VS Code
\1 / \2Group 1 / 2Python, sed, grep/Perl-style tools
$<year>Named groupJavaScript
\g<year>Named groupPython
${year}Named groupC#
${1}0Group 1 followed by a literal 0 (not group 10)PHP, C#
$& / $0The whole match$&: JavaScript, C#; $0: PHP, C#; Python: \g<0>
$$A literal $JavaScript, C#
\u$1 / \U$1 / \L$1Group 1 with first letter uppercase / all uppercase / all lowercaseVS Code (replace box)
"2026-10-12".replace(/(?<y>\d{4})-(?<m>\d\d)-(?<d>\d\d)/, "$<d>/$<m>/$<y>"); // "12/10/2026"
"hello world".replace(/\b\w/g, (c) => c.toUpperCase());                     // "Hello World"

Ready-Made Patterns #

Tested with JavaScript and Python. They check the format, not whether something really exists (use a library or send a test email for that). In a JavaScript literal /.../, escape / as \/.

PurposePattern
Whole number^-?\d+$
Decimal number (. or ,)^-?\d+(?:[.,]\d+)?$
Email address (practical check)^[\w.+-]+@[\w-]+(?:\.[\w-]+)*\.[A-Za-z]{2,}$
URL (http/https) (flag i)^https?://[^\s/$.?#].[^\s]*$
Domain name (flag i)^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$
IPv4 address (0–255 per part)^(?:(?: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)$
IPv6 address (full form only) (flag i)^(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$
Date YYYY-MM-DD^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$
Date DD/MM/YYYY^(?:0[1-9]|[12]\d|3[01])/(?:0[1-9]|1[0-2])/\d{4}$
Time HH:MM or HH:MM:SS (24 h)^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$
Hex color #fff / #ffffff (flag i)^#(?:[0-9a-f]{3}){1,2}$
UUID (flag i)^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
URL slug (git-bash)^[a-z0-9]+(?:-[a-z0-9]+)*$
Username, 3–16 letters/digits/_^[a-zA-Z0-9_]{3,16}$
Password: 8+ chars, lower, upper, digit, symbol^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$
MAC address (flag i)^(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}$
Version number (SemVer)^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$
Belgian postal code^[1-9]\d{3}$
Belgian phone number^(?:\+32|0032|0)\s?[1-9](?:[\s./-]?\d){7,8}$
Belgian IBAN^BE\d{2}(?:\s?\d{4}){3}$
Belgian VAT / company number^BE\s?[01]\d{3}\.?\d{3}\.?\d{3}$
HTML tag (for quick searches, not parsing) (flag i)</?[a-z][a-z0-9-]*\b[^>]*>
Image file extension (flag i)\.(jpe?g|png|gif|webp)$
Trailing whitespace (flag m)[ \t]+$
Repeated word (the the) (flag i)\b(\w+)\s+\1\b
YouTube video id (group 1)(?:youtube\.com/watch\?v=|youtu\.be/)([\w-]{11})

Differences Between Languages #

FeatureJavaScriptPython rePHP (PCRE)C# (.NET)grep -E
Named group(?<n>...)(?P<n>...)(?<n>...)(?<n>...)no
Lookbehindany widthfixed widthfixed widthany widthno (use grep -P)
\d \w \syes (ASCII)yes (Unicode)yesyes (Unicode)\w \s yes (GNU), \d no: use [0-9]
\p{L}with uno (regex package: yes)with uyesno
Possessive / atomicnoyes (3.11+)yesatomic (?>...) onlyno
Inline flags (?i)(?i:...) (ES2025)yesyesyesno (grep -i)
Pattern written as/x/g or new RegExp("x", "g")r"x" (raw string)"/x/i" (with delimiters)@"x" (verbatim string)'x'

JavaScript #

CodeWhat it does
/^\d+$/.test(s)Does it match? (true / false)
s.match(/\d+/)First match with groups, or null
s.match(/\d+/g)Array of all matches (no groups), or null
[...s.matchAll(/(\w)(\d)/g)]All matches with groups (needs g)
m.groups.yearA named group from match / exec
s.replace(/a/g, "b") / s.replaceAll(/a/g, "b")Replace all (replace without g only replaces the first)
s.split(/\s*,\s*/)Split on a pattern: "a , b,c" gives ["a", "b", "c"]
s.search(/\d/)Index of the first match, or -1
new RegExp(`^${word}$`, "i")Build a pattern from a variable (escape user input first!)
RegExp.escape(text)Escape special characters in user input (ES2025; recent browsers, Node 24+)

A /g regex remembers lastIndex: calling re.test() twice on the same string can give true then false. Use a new regex or drop g for tests.

Python #

CodeWhat it does
import reThe built-in module
re.search(r"\d+", s)First match anywhere, or None
re.match(r"\d+", s) / re.fullmatch(r"\d+", s)Match at the start only / the whole string must match
m.group() / m.group(1) / m.group("year")Whole match / group 1 / named group
re.findall(r"\d+", s)List of all matches (strings, or tuples if there are groups)
re.finditer(r"\d+", s)Iterator of match objects (with positions)
re.sub(r"(\w+) (\w+)", r"\2 \1", s)Replace ("hello world" gives "world hello")
re.sub(r"\d", lambda m: m[0] * 2, s)Replace with a function
re.split(r"\s*,\s*", s)Split on a pattern
pattern = re.compile(r"\d+", re.I)Compile once, reuse many times
re.escape(text)Escape user input: "1.5+2" gives 1\.5\+2

Always use raw strings r"...": in a normal string "\b" is a backspace, not a word boundary.

PHP #

CodeWhat it does
preg_match('/^\d+$/', $s)1 if it matches, 0 if not (false on a pattern error)
preg_match('/(?<y>\d{4})/', $s, $m)Groups end up in $m ($m[1], $m['y'])
preg_match_all('/\d+/', $s, $m)All matches: $m[0] is the list; returns the count
preg_replace('/(\w+) (\w+)/', '$2 $1', $s)Replace (single quotes, so PHP doesn't read $2 as a variable)
preg_replace_callback('/\b\w/', fn($m) => strtoupper($m[0]), $s)Replace with a function
preg_split('/\s*,\s*/', $s)Split on a pattern
preg_quote($text, '/')Escape user input (pass your delimiter)
'/é/u'u flag: needed for UTF-8 text and \p{L}

The pattern is a string with delimiters: '/x/i', '#https?://#' (use # or ~ when the pattern contains /).

C# (.NET) #

CodeWhat it does
using System.Text.RegularExpressions;The namespace
Regex.IsMatch(s, @"^\d+$")Does it match?
Regex.Match(s, @"(?<y>\d{4})").Groups["y"].ValueA named group from the first match
foreach (Match m in Regex.Matches(s, @"\d+"))Loop over all matches
Regex.Replace(s, @"(\w+) (\w+)", "$2 $1")Replace
Regex.Split(s, @"\s*,\s*")Split on a pattern
Regex.Escape(text)Escape user input
new Regex(pattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1))With options and a timeout against slow patterns
[GeneratedRegex(@"\d+")] private static partial Regex Digits();Pattern compiled at build time (.NET 7+, fastest)

Use verbatim strings @"..." so backslashes stay single.

Command Line & Editors #

ToolHow
grep -E 'colou?r' fileExtended regex (+, ?, |, () without backslashes)
grep -P '\d+(?=€)' filePerl-style: \d, lookarounds (GNU grep)
grep -oE '[0-9]+' filePrint only the matching part
grep -rniE 'todo|fixme' src/Recursive, case-insensitive, with line numbers
sed -E 's/(\w+) (\w+)/\2 \1/' fileReplace (add -i to edit the file in place, g after the last / for all matches)
rg 'pattern'ripgrep: fast recursive search, Rust regex syntax (no lookarounds unless -P)
VS Code Ctrl + F / Ctrl + HClick .* (or Alt + R) to use regex; $1 in the replace box
VS Code Ctrl + Shift + FRegex search across all files

Performance & Pitfalls #

ProblemFix
(a+)+$ or (.*)* on a long non-matching text hangsNested quantifiers backtrack exponentially ("ReDoS"): rewrite without nesting, use atomic/possessive groups or a timeout
User input inside a pattern breaks or becomes a security holeEscape it first (re.escape, preg_quote, Regex.Escape, RegExp.escape)
.* grabs too muchUse a lazy .*? or a negated class like [^"]* (faster and clearer)
Validation accepts garbageAnchor with ^...$ (or fullmatch in Python)
. doesn't match across linesAdd the s flag, or use [\s\S] in older JavaScript
Parsing HTML, JSON or email addresses perfectlyUse a real parser or library; regex is fine for quick searches

Tips #

  • Build and test patterns on regex101.com: pick the right flavor (PCRE, Python, ECMAScript, .NET) and it explains every part.
  • Start simple and add cases one by one; write a few should-match and should-not-match examples first.
  • Prefer specific classes over .: [^,]+ says "up to the next comma" and avoids surprises.
  • Use non-capturing (?:...) when you only need grouping, and named groups when you have more than two.
  • Comment long patterns: Python re.X, PHP and .NET x let you split a pattern over lines with comments.
  • Regex can't count nesting (balanced brackets) or validate dates fully (2026-02-31 passes the date pattern): check those in code.