Cheatsheet Regex
Expressões regulares — padrões de pesquisa e manipulação de texto
Regex
Basic Characters
Literal characters
abc # matches "abc" 123 # matches "123" Hello World # matches "Hello World" # Matching is sequential: # "abc" is found in "xabcx" at position 1
Literal characters match the text exactly. The search is sequential and case-sensitive by default. Without metacharacters, each letter is an individual token.
Metacharacters
# Metacharacters (have special meaning):
. \ + * ? [ ] ( ) { } ^ $ | /
# To use the literal, escape:
\. \+ \* \? \[ \] \( \)
\{ \} \^ \$ \| \/ \\
# Inside [...], only ] \ ^ - need escapingThese characters control the pattern logic. ^ and $ are anchors, * and + are quantifiers. Inside [...], most lose their special meaning. Use \Q...\E to escape entire blocks.
Dot (wildcard)
. # any character (except \n) a.c # "abc", "a1c", "a c", "a-c" ... # any sequence of 3 chars a.c.e # "abcde", "a1c2e" # With flag s (dotall): # . also matches \n
The . is the universal wildcard — it matches any character except newline. With the s flag (dotall), it also includes \n. Use it sparingly to avoid unwanted matches.
Unicode and properties
\p{L} # any letter (Unicode)
\p{Lu} # uppercase letter
\p{Ll} # lowercase letter
\p{N} # any number
\p{P} # punctuation
\p{Sc} # currency symbol (€, $, £)
\P{L} # NOT letter (negated)
# Example: name with accents
^[\p{L}\s]+$\p{...} accesses Unicode properties. \p{L} includes accented letters (á, ã, ç). \P (uppercase) negates the property. Essential for accented text. Requires the u flag in most languages.
Shorthand classes
\d # digit [0-9] \D # NOT digit (equivalent to [^\d]) \w # word [a-zA-Z0-9_] \W # NOT word \s # whitespace (space, tab, \n, \r) \S # NOT whitespace
Shortcuts for common sets. \d equals [0-9], \w equals [a-zA-Z0-9_]. The uppercase version is always the negation. Prefer these shortcuts over manual classes for readability.
Escapes and special chars
\t # tab \n # newline (LF) \r # carriage return (CR) \0 # null character # Escape metacharacters: \. # literal dot \\ # literal backslash \( # literal parenthesis \[ # literal square bracket
The backslash \ escapes metacharacters to make them literal. \t, \n and \r are control characters. Whenever you need a literal ., * or ?, escape it with \.
Quantifiers
Basic quantifiers
* # 0 or more times + # 1 or more times ? # 0 or 1 (optional) a* # "", "a", "aa", "aaa" a+ # "a", "aa" (does not match "") colou?r # "color" or "colour" https? # "http" or "https"
* accepts zero occurrences, + requires at least one. ? makes the element optional. These are the most used quantifiers. Apply them to characters, classes or groups.
Possessive (atomic)
# Possessive quantifiers (Java, PCRE):
*+ # 0 or more, no backtrack
++ # 1 or more, no backtrack
?+ # 0 or 1, no backtrack
{n,m}+
# Atomic group (universal alternative):
(?>...)
# Example:
"a++a" # NEVER matches "aa"
# (the ++ does not give back the "a")Possessive quantifiers (++, *+) never backtrack — faster but less flexible. (?>...) creates an atomic group. Avoids catastrophic backtracking. Supported in Java, PCRE and .NET (not in JS).
Repetition with braces
{n} # exactly n times
{n,} # n or more times
{n,m} # between n and m times
\d{4} # exactly 4 digits (year)
\d{2,4} # 2 to 4 digits
\w{3,} # 3 or more word characters
[A-Z]{1,3} # 1 to 3 uppercase (initials){n} sets the exact amount. {n,} defines a minimum with no upper limit. {n,m} defines a range. More precise than * or + when you know the limits. Ideal for codes, dates and IDs.
Common combinations
\d{1,3} # 1 to 3 digits (IP octet)
[a-z]+ # one or more lowercase
\s* # zero or more spaces
ba(na)* # "ba", "bana", "banana"
(\d{2}[./-]){2}\d{4} # date with separators
\w+@\w+ # user@host (simplified)Combine quantifiers with classes for real patterns. \s* accepts optional spaces. (na)* repeats a group. Use {1,3} for IP octets. The practice is to combine these pieces into compound patterns.
Greedy
# By default, quantifiers are greedy:
# they try to match the MAXIMUM possible
<.+> # in "<b>text</b>"
# grabs "<b>text</b>" (everything!)
".+" # in 'says "hello" and "goodbye"'
# grabs '"hello" and "goodbye"' (everything!)
# Greedy advances to the end and backtracks
# (backtracking) to find a matchGreedy mode (default) consumes the much the possible. The engine advances to the end and does backtracking to satisfy the rest of the pattern. It can cause unexpected results with multiple occurrences on the same line.
Lazy
# Adding ? makes the quantifier lazy:
# it matches the MINIMUM possible
<.+?> # in "<b>text</b>"
# grabs "<b>" (only the first!)
".+?" # in 'says "hello" and "goodbye"'
# grabs '"hello"' (only the first!)
# Lazy versions: *? +? ?? {n,m}?Lazy mode (? after a quantifier) stops at the first valid match. .+? is essential for extracting content between delimiters. More predictable than greedy for HTML and quoted strings.
Anchors and Limits
Start and end of string
^ # start of string (or line with flag m) $ # end of string (or line with flag m) ^Hello # starts with "Hello" world$ # ends with "world" ^\d+$ # whole string is only digits ^$ # empty string
^ anchors at the start, $ at the end. Without these anchors, the pattern matches at any position. ^\d+$ validates that the whole string is digits. Essential for validating complete inputs.
Positions and position lookaround
# Anchors are "zero-width assertions":
# they match a POSITION, not a char
^ # position before the 1st char
$ # position after the last char
\b # position between \w and \W
# All can be combined:
^\b[A-Z]\w+\b$ # capitalized word
# (whole string)Anchors consume no characters — they only check the position. They are zero-width assertions, like lookarounds. Combine ^ + \b + $ for strict validations. Don't confuse position with content.
Word boundaries
\b # word boundary (transition \w / \W) \B # position that is NOT a boundary \bcat\b # "cat" isolated (not "cats") \b\d+\b # isolated whole number \Bcat # "cat" in the middle: "concat" \b[A-Z] # word starting with uppercase
\b detects the boundary between \w and \W (or start/end). It consumes no characters — it is zero-width. \bcat\b avoids false positives in "cats" or "concat". \B is the inverse.
Multiline (flag m)
# Without flag m: ^ and $ = start/end of string # With flag m: ^ and $ = start/end of each line # With flag m enabled: ^# # lines that start with # \.$ # lines that end with a dot ^$ # empty lines # In JS: /^#/m # In PHP: "/^#/m" # In Python: re.M
The m flag (multiline) makes ^ and $ act on each line, not just the whole string. Ideal for processing config files or logs. Without m, ^ only matches at the absolute start.
Absolute start and end
\A # absolute start of string
\Z # absolute end (before final \n)
\z # absolute end (no exceptions)
# Difference from ^ and $:
# \A and \z ignore the m flag
# Always = whole string
# Example: validate complete token
\A[a-f0-9]{32}\z\A and \z are absolute anchors — not affected by the m flag. \Z accepts an optional final \n. Use them when you need full validation even with multiline active. Supported in Ruby, Python, PCRE and Java.
Classes and Switching
Character class
[abc] # a, b or c [a-z] # lowercase letter [A-Z] # uppercase letter [0-9] # digit [a-zA-Z0-9] # any alphanumeric [aeiou] # lowercase vowels
Square brackets [...] define a set — it matches ONE character from the set. [a-z] is a range. Combine ranges: [a-zA-Z]. Inside [], most metacharacters lose their meaning.
POSIX classes
# POSIX (used in grep, sed, awk): [[:alpha:]] # letters (= [a-zA-Z]) [[:digit:]] # digits (= [0-9]) [[:alnum:]] # alphanumeric [[:space:]] # spaces [[:upper:]] # uppercase [[:lower:]] # lowercase [[:punct:]] # punctuation # Example: grep "[[:upper:]]" file.txt
POSIX classes are used in Unix tools (grep, sed, awk). Syntax: [[:name:]] (double bracket). [[:alpha:]] equals [a-zA-Z]. Not supported in JavaScript. In PCRE, prefer \p{L}.
Class negation
[^abc] # any char EXCEPT a, b, c [^0-9] # not digit (= \D) [^a-z] # not lowercase [^aeiou] # consonants (and other chars) [^\s] # not space (= \S) # ^ only negates if it is the 1st char inside []
^ the the first character inside [...] negates the set. [^0-9] equals \D. Note: [^abc] still matches newline — use [^abc\n] to exclude it. The negation applies to a single character.
Subtraction and intersection
# Java supports class operations: [a-z&&[^aeiou]] # lowercase consonants [\w&&[^_]] # \w without underscore [0-9&&[^0]] # digits 1-9 # .NET supports subtraction: [\w-[0-9]] # \w without digits # In PCRE/JS, simulate with negation: (?![aeiou])[a-z] # lowercase consonant
Java allows && (intersection) and internal [^...] for subtraction. .NET uses [-...]. In JavaScript and PCRE, simulate with negative lookahead. Useful for filtering subsets of broad classes.
Ranges and combinations
[0-9a-fA-F] # hexadecimal
[a-z0-9_] # slug (lowercase, numbers, _)
[\w.-] # word with dot and hyphen
[\p{L}\s] # Unicode letters and spaces
[^\s<>] # chars that are not space or tags
# Hyphen at end/start is literal:
[-abc] or [abc-] # literal hyphenRanges use - between two chars (a-z). For a literal hyphen, place it at the start or end: [-abc]. Combine multiple ranges: [0-9a-fA-F]. \w inside [] expands to [a-zA-Z0-9_].
Alternation (OR)
cat|dog # "cat" or "dog" yes|no|maybe # three options (jpg|png|gif) # image extensions # | has the LOWEST precedence: # "gra|eta" matches "gra" OR "eta" # NOT "gr(a|e)ta" # Use groups to limit: gr(a|e)ta # "grata" or "greta"
The pipe | works the a logical OR. It has the lowest precedence — it separates everything on the left from everything on the right. Use (...) to limit the scope. (jpg|png|gif) is more efficient than [jpg] for complete strings.
Groups and Capture
Capturing group
(\d{2})/(\d{2})/(\d{4})
# Capture: day=$1, month=$2, year=$3
# In replacement:
# $1-$2-$3 → "25-12-2024"
# In JS: match[1], match[2], match[3]
# In PHP: $m[1], $m[2], $m[3]
# In Python: group(1), group(2)Parentheses (...) capture the matched text. Each group gets a number ($1, $2...). Use them in substitutions to rearrange. $0 is always the full match. Numbering follows the order of the opening parentheses.
Conditional groups
# (?(condition)yes|no) — PCRE and .NET # If group 1 captured, require closing: (<)?(\w+)(?(1)>) # Matches "abc" or "<abc>" but not "<abc" # If lookahead passed: (?=(\d))\1+[a-z] # Only if it starts with a digit # Rarely used — prefer separate logic
Conditional groups (?(cond)yes|no) branch based on a capture or assertion. (?(1)...) checks whether group 1 captured. Supported in PCRE and .NET, not in JavaScript. Rarely needed — prefer separate patterns for clarity.
Non-capturing group
(?:...) # groups WITHOUT capturing
(?:ha)+ # "ha", "haha", "hahaha"
(?:https?|ftp):// # protocol without capturing
(?:\d{2}[./-]){2}\d{4} # date (no groups)
# Advantages:
# - Faster (does not store in memory)
# - Does not shift the $n numbering(?:...) groups to apply quantifiers or alternation without creating a capture. More memory-efficient. Does not affect the numbering of $1, $2. Use it whenever you don't need to reference the group's content.
Branch reset
# (?|...) — PCRE and Perl
# Resets numbering in each alternative:
(?|(\d{4})-(\d{2})|(\d{2})/(\d{4}))
# $1 and $2 in both alternatives
# Without branch reset: $1,$2 or $3,$4
# Useful for multiple formats:
(?|cat|dog|bird) # all are $1(?|...) (branch reset) resets the group numbering in each alternative. Without it, alternatives create sequential groups. Supported in PCRE and Perl. It simplifies substitutions when multiple formats produce the same result.
Named group
(?<name>...) # PCRE/JS/.NET syntax
(?P<name>...) # Python syntax
(?<day>\d{2})/(?<month>\d{2})/(?<year>\d{4})
# Access:
# JS: match.groups.day
# PHP: $m['day']
# Python: group('day')
# Subst: ${day}-${month}-${year}Named groups ((?<name>...)) are more readable than $1, $2. Python uses (?P<name>...). Access by name instead of number. Ideal for complex patterns with many captures. Works in substitutions: ${name}.
Backreference
# Reference a previous capture in the pattern: \1 \2 \3 # (PCRE, Python, Java) $1 $2 $3 # (JavaScript in pattern: \1) (\w+) \1 # repeated word: "hello hello" (["']).*?\1 # matched quotes: "text" or 'text' <(\w+)>.*?</\1> # matched HTML tag: <b>...</b>
\1 references the text captured by group 1. It ensures the same delimiter closes. (["']).*?\1 matches quotes of the same type. <(\w+)>.*?</\1> validates matched HTML tags. It only works within the same pattern.
Lookaround
Positive lookahead
(?=...) # followed by... (without consuming) \d+(?=€) # number before €: "100" in "100€" \w+(?=\() # function name before ( (?=.*\d) # string that contains a digit # Does not consume: the match is only the number # "100€" → match = "100" (€ not included)
(?=...) checks that the text ahead matches, without including it in the match. It is zero-width. Ideal for validations: (?=.*\d)(?=.*[A-Z]) requires a digit AND an uppercase letter. The cursor does not advance — the next part of the pattern starts at the same position.
Lookaround summary
(?=X) X ahead (positive)
(?!X) X NOT ahead (negative)
(?<=X) X behind (positive)
(?<!X) X NOT behind (negative)
# All are zero-width (do not consume)
# They can be combined:
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$
# Password: digit + lower + upper + 8+ charsThe 4 lookarounds are zero-width assertions. They neither capture nor consume characters. Combine them for multi-criteria validations (passwords). (?=.*X) is the "must contain X" pattern. Support: PCRE, Java, .NET, Python 3; JS only lookahead, and lookbehind since ES2018.
Negative lookahead
(?!...) # NOT followed by... foo(?!bar) # "foo" without "bar" after \d+(?!€) # number without € after (?!.*admin)^.*$ # line without "admin" # Password without sequences: ^(?!.*123)(?!.*abc).+$
(?!...) rejects if the text ahead matches. Does foo(?!bar) match "foobar"? No! It matches "foo" in "foobaz". Useful for excluding specific patterns. Combine several for complex validations (passwords, filters).
Positive lookbehind
(?<=...) # preceded by... (without consuming)
(?<=R\$)\d+ # value after "R$": "50" in "R$50"
(?<=@)\w+ # domain after @
(?<=https://).+ # URL without the protocol
# In JS (ES2018+):
/(?<=€)\d+/.exec("100€") // ERROR!
/(?<=€)\d+/.exec("€100") // "100"(?<=...) checks the text BEFORE the current position. The match does not include the prefix. In JavaScript, it requires ES2018+. The lookbehind must be fixed-length in Java. Ideal for extracting values after symbols or prefixes.
Negative lookbehind
(?<!...) # NOT preceded by...
(?<!un)happy # "happy" without "un" before
(?<!\d)\d{3} # 3 digits without a digit before
(?<![/\w])\. # dot not in path/URL
# Exclude subdomains:
(?<!www\.)\b[\w.]+\b(?<!...) rejects if the preceding text matches. (?<!un)happy matches "happy" but not "unhappy". Useful to avoid false positives. Like positive lookbehind, it must be fixed-length in Java and .NET.
Practical Examples
^[\w.+-]+@[\w-]+\.[\w.-]+$
# Accepts: name@domain.com
# name.tag@sub.domain.com
# user+tag@gmail.com
# More restrictive version:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$Practical pattern for email validation. [\w.+-]+ covers the local part. [\w.-]+ the domain. Don't try to validate 100% of RFC 5322 — use email confirmation. The restrictive version limits special characters.
NIF and postal code (PT)
# Portuguese NIF (9 digits, starts 1-3,5,6,8):
\b[123568]\d{8}\b
# NIF with check digit validation:
# (requires mod 11 somethingrithm — not regex alone)
# PT postal code:
\b\d{4}-\d{3}\b
# Accepts: 1000-001, 4400-123
# PT IBAN:
\bPT50\s?(\d{4}\s?){5}\d{1}\b[123568]\d{8} covers the valid first digits of a NIF. Full validation requires the mod 11 somethingrithm (not possible with regex alone). The postal code is \d{4}-\d{3}. For IBAN, the pattern is long — also validate the checksum.
Phone (Portugal)
# PT mobile:
^(\+?351)?[\s]?9\d{2}[\s]?\d{3}[\s]?\d{3}$
# Accepts: 912345678
# +351 912 345 678
# 351912345678
# PT landline:
^(\+?351)?[\s]?2\d{2}[\s]?\d{3}[\s]?\d{3}$+?351 makes the country code optional. 9\d{2} for mobiles (they start with 9). [\s]? accepts optional spaces between groups. Landlines start with 2. For real validation, use the libphonenumber library.
IPv4
# Full version (0-255 per octet):
\b((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)\b
# Simple version (no range validation):
\b\d{1,3}(\.\d{1,3}){3}\b
# Simplified IPv6:
\b([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b25[0-5]|2[0-4]\d|1?\d?\d validates each octet (0-255). (...){3} repeats the first 3 octets with a dot. The simple version accepts 999.999.999.999 — use the full one for validation. Real IPv6 is far more complex (:: compression).
URL
https?://[\w.-]+(?:\.[\w]{2,})+[/\w\-._~:/?#\[\]@!$&'()*+,;=%]*
# Accepts:
# https://example.com
# http://sub.domain.com/page?q=1
# Simple version (detection):
\bhttps?://\S+\bhttps? covers http and https. [\w.-]+ is the domain. The simple version \bhttps?://\S+\b is enough for detection. For real parsing, use native functions (parse_url, new URL()). Regex is not ideal for complex URLs.
Strong password
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$
# Criteria:
# (?=.*[a-z]) → at least 1 lowercase
# (?=.*[A-Z]) → at least 1 uppercase
# (?=.*\d) → at least 1 digit
# (?=.*[@$!%*?&]) → at least 1 special
# {8,} → minimum 8 charactersEach (?=.*X) is a lookahead that requires one criterion. All must pass (logical AND). The final pattern [A-Za-z\d@$!%*?&]{8,} limits the accepted characters. Add more lookaheads for more rules. Always test with edge cases.
Date and time
# Date DD/MM/YYYY (with validation):
\b(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{4}\b
# Time HH:MM (24h):
\b([01]\d|2[0-3]):[0-5]\d\b
# ISO date:
\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])
# Full timestamp:
\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}0[1-9]|[12]\d|3[01] validates days 01-31. [01]\d|2[0-3] limits hours to 00-23. It does not validate days per month (Feb 30 passes). For real validation, combine with DateTime or checkdate(). The ISO format is the safest.
HTML and tags
# Extract the content of a tag: <(h[1-6])>(.*?)</\1> # Remove all tags: <[^>]+> # Specific attribute: href=["']([^"']+)["'] # HTML comment: <!--[\s\S]*?--> # WARNING: regex is NOT an HTML parser! # Use DOMDocument / cheerio / BeautifulSoup
<(h[1-6])>(.*?)</\1> extracts headings with a backreference. <[^>]+> removes tags (simplified). Regex fails on nested or malformed HTML. For real parsing, use a DOM parser. Regex is for simple, controlled tasks.
Flags and Languages
Common flags
g # global — all occurrences (not just 1st) i # case-insensitive (ignores case) m # multiline (^ and $ per line) s # dotall (. includes \n) u # Unicode (UTF-8) x # extended (ignores spaces and # comments) # Combinations: /pattern/gim # In Python: re.IGNORECASE | re.MULTILINE
g returns all matches, not just the first. i makes it case-insensitive. m makes ^/$ act per line. s makes . include newline. x allows readable patterns with comments. Combine the needed.
Best practices
# 1. Be specific (avoid generic .*) # Bad: <.*> # Good: <[a-z]+>[^<]*</[a-z]+> # 2. Anchor when possible (^...$) # 3. Use \d \w \s instead of [0-9] etc. # 4. Test on regex101.com # 5. Beware catastrophic backtracking: # Bad: (a+)+$ # Good: a+$ # 6. Document complex patterns # 7. Use the x flag for long patterns
Avoid .* when you can be specific. (a+)+ causes catastrophic backtracking — simplify it. Test on regex101.com with a step-by-step explanation. Use the x flag to document inline. For input validation, always anchor with ^ and $.
JavaScript
const re = /\d+/g;
"a1b22".match(re); // ["1", "22"]
/hello/i.test("HELLO"); // true
text.replace(/\s+/g, " "); // collapse spaces
// With constructor:
new RegExp("\\d+", "g");
// Named groups (ES2018):
/(?<year>\d{4})-(?<month>\d{2})/
// match.groups.yearIn JavaScript, a regex goes between /.../flags. .match() returns an array, .test() returns a boolean. .replace() substitutes. new RegExp() for dynamic patterns (escape \). Named groups via match.groups.
PHP (PCRE)
preg_match("/\d+/", $txt, $m); // 1st match
preg_match_all("/\w+/", $txt, $m); // all
preg_replace("/\s+/", " ", $txt); // replace
preg_split("/[,;\s]+/", $txt); // split
// Delimiters: / # ~
// Flags after the delimiter: "/pattern/gi"
// Named groups:
preg_match('/(?<year>\d{4})/', $txt, $m);
echo $m['year'];PHP uses preg_* functions with delimiters (/, #, ~). preg_match returns 1 or 0. preg_match_all fills an array with all matches. Named groups are accessed by key. Always escape the delimiter inside the pattern.
Python
import re re.search(r"\d+", text) # 1st match re.findall(r"\w+", text) # list of all re.sub(r"\s+", " ", text) # replace re.split(r"[,;]", text) # split # Raw string r"..." avoids double escaping # Flags: re.IGNORECASE, re.MULTILINE, re.DOTALL # Compile to reuse: pattern = re.compile(r"\d+", re.IGNORECASE) pattern.findall(text)
Python uses the re module with raw strings r"..." (avoids \). re.search for the first, re.findall for all. re.compile optimizes reused patterns. Flags the constants: re.I, re.M, re.S.