Nexinon

Regex Cheatsheet

Regex syntax explained symbol by symbol, with real examples and ready-to-copy patterns.

28 of 28 symbols
Anchors — position in the text
Symbols that don't match a character, but a position — start/end of string or line, word boundary.

^

Start of string/line

Marks a position, not a character — without the m flag, the start of the whole string; with m, the start of each line.

Matches: cat is here
Doesn't match: the cat

$

End of string/line

The counterpart to ^ — without the m flag, the end of the whole string; with m, the end of each line.

Matches: the cat
Doesn't match: cats

\b

Word boundary

A position between a word character (\w) and one that isn't — it consumes no character, only marks the transition.

Matches: a cat sat
Doesn't match: concatenate

\B

Non-word boundary

The opposite of \b — matches any position that is not a word boundary (both sides alike: both word characters, or both non-word).

Matches: concatenate
Doesn't match: a cat sat
Character Classes — what kind of character
Symbols that match a single character from a set — digit, letter, whitespace, or a hand-defined set.

\d

Digit

Equivalent to [0-9] — matches any digit from 0 to 9.

Matches: abc123
Doesn't match: abc

\w

Word character

Letters, digits, and underscore — equivalent to [A-Za-z0-9_].

Matches: hello_123!!!
Doesn't match: !!!

\s

Whitespace

Space, tab, line break, and other whitespace characters.

Matches: a b
Doesn't match: ab

.

Any character

Matches any single character, except a line break — unless the s flag is active.

Matches: cat and cot
Doesn't match: ct

[...]

Character class

Matches any one of the characters listed inside the brackets — accepts ranges, like [a-z].

Matches: hello
Doesn't match: xyz

[^...]

Negated class

The ^ right after the bracket negates the class — matches any character that is NOT in the list.

Matches: a1
Doesn't match: 123
Quantifiers — how many times
Control how many times the preceding element can repeat — from zero to unlimited.

*

Zero or more

The preceding element may appear zero, one, or many times in a row.

Matches: abbbc
Doesn't match: abxc

+

One or more

Like *, but requires at least one occurrence of the preceding element.

Matches: abbc
Doesn't match: ac

?

Zero or one (optional)

Marks the preceding element as optional — present or absent, both match.

Matches: color and colour
Doesn't match: colouur

{n,m}

Repetition range

Between n and m occurrences of the preceding element — {n} is exact, {n,} is "at least n", {,m} is "at most m".

Matches: a123b
Doesn't match: a1b

*? +?

Lazy quantifier

By default, *, +, and {n,m} are greedy (they match as much as possible) — an extra ? right after makes the quantifier lazy, matching as little as possible.

Matches: <a><b>
Doesn't match: no tags here
Groups & Alternation — grouping and choosing
Group part of the pattern to apply a quantifier, capture the matched text, or choose between alternatives.

(...)

Capturing group

Groups part of the pattern and stores the matched text for later use (backreference, replacement).

Matches: ababab
Doesn't match: xyz

(?:...)

Non-capturing group

Groups like (...), but without generating a numbered capture — useful when only the grouping matters, not the value.

Matches: ababab
Doesn't match: xyz

|

Alternation (OR)

Matches one alternative or the other — tries the left one first.

Matches: I have a dog
Doesn't match: I have a bird

(?<name>...)

Named group

Like (...), but the capture gets a name (accessible as match.groups.name) instead of just a positional number.

Matches: Year: 2026
Doesn't match: Year: 26
Lookaround — checking without consuming
Check what comes before/after a position without including that text in the final match.

(?=...)

Positive lookahead

Matches a position followed by the given pattern, without consuming that text in the result.

Matches: 42px
Doesn't match: 42em

(?!...)

Negative lookahead

Matches a position NOT followed by the given pattern.

Matches: 42em
Doesn't match: no numbers here

(?<=...)

Positive lookbehind

Matches a position preceded by the given pattern, without consuming that text in the result.

Matches: $42
Doesn't match: 42

(?<!...)

Negative lookbehind

Matches a position NOT preceded by the given pattern.

Matches: 42 reais
Doesn't match: no numbers here
Flags — how the engine processes the pattern
Modifiers that change the behavior of the whole expression — case sensitivity, all occurrences, multiple lines.

i

i — case-insensitive

Ignores the difference between uppercase and lowercase across the whole pattern.

Matches: CAT
Doesn't match: dog

g

g — global

Finds every occurrence in the text, not just the first one.

Matches: banana
Doesn't match: xyz
3 occurrences found with the g flag

m

m — multiline

Makes ^ and $ match the start/end of each line, not just of the whole string.

Matches: a b
Doesn't match: xyz qrs

s

s — dotall

Makes . also match a line break, which it ignores by default.

Matches: a b
Doesn't match: ab

u

u — unicode

Treats the pattern as a sequence of Unicode code points, not UTF-16 units — required to correctly match characters outside the BMP (like most emoji) and to use \p{...}.

Matches: 😀
Doesn't match: ab
Ready-made pattern library

Tested, ready-to-copy regex — each one with an honest caveat when a relevant limitation exists.

Email

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

Basic email address shape: something@something.something, no spaces.

Valid examples:
user@example.com
first.last+tag@sub.example.co.uk
A pragmatic approximation, not a 100% RFC 5322-compliant validation — the full email spec is notoriously complex (it even allows quoted addresses and exotic characters that almost no real provider accepts). To confirm an email actually exists, only sending a confirmation message works — no regex can do that.

URL

/^https?:\/\/[^\s/$.?#][^\s]*$/

An http/https URL with an optional path, query string, and fragment.

Valid examples:
https://example.com
http://example.com/path?query=1#frag
Accepts any reasonably shaped http/https URL, but doesn't confirm the domain actually exists, nor does it strictly follow RFC 3986 (which allows much more elaborate structures) — meant for validating a form field, not for a full URL parser.

IPv4

/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/

A valid IPv4 address — four octets from 0 to 255, separated by dots.

Valid examples:
192.168.0.1
255.255.255.255
0.0.0.0

IPv6

/^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$/

An IPv6 address in its full form — eight groups of up to 4 hex digits, separated by colons.

Valid examples:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
Covers only the full form (8 groups) — not the compressed form (::, which replaces a run of all-zero groups), as in 2001:db8::1: a real, valid IPv6 address that this regex rejects. Full IPv6 validation is notoriously complex, even for dedicated libraries.

Phone number (BR)

/^(?:\+55\s?)?\(?\d{2}\)?[\s-]?\d{4,5}-?\d{4}$/

A Brazilian phone number with area code, with or without +55, parentheses, and a hyphen.

Valid examples:
(11) 91234-5678
11912345678
+55 11 91234-5678
1112345678
Covers the most common ways people type it — doesn't validate whether the area code is a real Brazilian one, nor whether the number is actually in use.

CEP (postal code)

/^\d{5}-?\d{3}$/

Brazilian postal code (CEP) — 8 digits, with an optional hyphen between the fifth and sixth.

Valid examples:
01310-100
01310100
Validates the format only — doesn't confirm the CEP actually exists or which address it corresponds to.

Hex color

/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/

A CSS hex color — 3, 6, or 8 digits (the last case includes the alpha channel).

Valid examples:
#fff
#a1b2c3
#a1b2c3ff

Date (ISO 8601)

/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/

A date in YYYY-MM-DD format, with month between 01-12 and day between 01-31.

Valid examples:
2026-08-25
2026-01-01
2026-12-31
Validates the format and each field's range, but not the real calendar — 2026-02-30 passes this regex (February never has 30 days), because confirming how many days a specific month has requires logic beyond what a regex alone can express.

This reference covers JavaScript

The syntax and behavior here are JavaScript/ECMAScript's own regex engine — what runs natively in any browser, with no library at all. Most of the syntax is nearly identical across dialects (PHP/Perl's PCRE, Python's re, .NET's regex), but a few points diverge in real ways: lookbehind — (?<=...) and (?<!...) — only reached PCRE in recent versions, and Python's re only in 3.11; named groups use (?<name>...) in JavaScript/PCRE/.NET, but (?P<name>...) in Python; POSIX classes like [:alpha:] exist in PCRE/POSIX but not in JavaScript, which uses \w/\d/\s instead. When porting a regex from another context to JavaScript (or vice versa), it's worth checking these points first.

How a regex engine actually searches

Behind every greedy quantifier, the engine tries to consume as much as possible and only backs off (backtracks) when the rest of the pattern can't match. That backing off has a cost: a poorly designed pattern with nested, ambiguous quantifiers over the same stretch of text (e.g. (a+)+b against a long string with no "b" at the end) can make the engine try an exponential number of combinations before giving up — the phenomenon known as "catastrophic backtracking", capable of freezing a page or an API for seconds or minutes with a relatively short input. Lazy quantifiers (see Quantifiers, above) don't eliminate the risk on their own, but avoiding nested quantifiers over the same character set is the most effective defense.

Capture groups in practice: .replace()

A capture group's practical value shows up in replacement: 'JavaScript'.replace(/(\w+)Script/, '$1.js') uses $1 to refer to whatever the first group captured. Named groups make this more readable in patterns with several groups: with (?<first>\w+)\s(?<last>\w+), a match's result carries match.groups.first and match.groups.last instead of match[1]/match[2] — the name survives any future reordering of the pattern, the positional number doesn't. Non-capturing groups (?:...) exist precisely to group without competing for a capture number when that capture will never be used.

Frequently asked questions

Because each regex engine evolved from a different tradition (Perl's PCRE, Python's re, JavaScript's native engine, .NET's regex) and converged on a very similar syntax core, but with edge-case details that diverged over decades — lookbehind, named groups, and POSIX classes are the most common sources of friction when porting a regex from one context to another (see "This reference covers JavaScript", above).

Only the format (11 or 14 digits, with or without punctuation) — not the check digit, which requires an arithmetic calculation over the preceding digits, something a regex alone can't express. To actually validate one, you need to run the check-digit algorithm (see Nexinon's CPF/CNPJ Generator/Validator).

Because RFC 5322 (the actual email address spec) allows formats that almost no real email provider accepts in practice — quoted addresses, comments inside the address, and other extremely rare edge cases. The regex in the pattern library above covers the shape that 99% of real emails follow, enough to validate a form field; the only way to know an email truly exists is to send a confirmation message.

The classic example is trying to extract an HTML tag: against '<a><b>', the greedy pattern <.+> matches the whole string '<a><b>' (consumes as much as possible, including the '>' in the middle), while the lazy <.+?> matches just '<a>' (stops as soon as the first valid alternative appears). Someone expecting to extract one tag at a time and using the greedy quantifier by mistake ends up with silently wrong results — the bug throws no error, it just returns more text than it should.

For clarity, and in large patterns with many groups, to avoid losing track of which capture number corresponds to which group — every (?:...) is one fewer captured group to mentally renumber when editing the pattern later. The performance impact is minimal in most cases; the real benefit is avoiding human confusion, not execution speed.

Technically yes, but with a real caveat: if that regex will process untrusted input (text coming from a user, for instance), a poorly designed pattern with nested quantifiers can suffer "catastrophic backtracking" (see "How a regex engine actually searches", above) and freeze your server with a relatively short, malicious input — a denial-of-service vector known as ReDoS. Testing the regex against deliberately adversarial input (long, almost-but-not-quite-valid strings) before using it in production is the simplest defense.

Nexinon Principles

Privacy

Your data never leaves your browser.

No account needed

Use it now, no account or password.

Free

No usage limits, no paid plan.

Trustworthy content

Full explanation behind every tool, not just the result.
See the live proof — Trust Center

Other Reference tools

View all