Most regular expressions in daily use are assembled from a small set of recurring pieces. Learning those pieces well is more productive than memorising complete patterns, because real requirements rarely match an example exactly.

This reference works through the constructs that come up constantly, with the caveats that make each one behave differently than expected in practice.

Matching any character

The dot matches any character except a newline. That exception surprises people processing multi-line text, where .* stops at the end of the first line rather than consuming the whole input.

To include newlines, enable the dot-all flag, written as s in most languages and Singleline in .NET. Where a flag is unavailable, the idiom [\s\S] matches any character by combining whitespace and its complement, which between them cover everything.

Regex match digits and structured numbers

The shorthand \d matches a digit and \d+ matches one or more. In several engines with Unicode enabled, \d also matches digits from other writing systems, so use [0-9] when you specifically mean ASCII digits, as in a numeric identifier.

Numbers with structure need more care. A decimal is roughly -?\d+(?:\.\d+)?, allowing an optional sign and an optional fractional part. Anchor it for validation, otherwise it will match the digits inside a longer string, and remember that thousands separators and exponent notation each require explicit handling.

Regex match whitespace and word characters

The shorthand \s matches spaces, tabs, and line breaks, and in Unicode mode also non-breaking spaces and other exotic separators. Its complement \S matches any non-whitespace character. Trimming and splitting on whitespace are the most common uses.

\w matches word characters: letters, digits, and the underscore. The underscore inclusion catches people out, and so does the ASCII-only default in some engines, which excludes accented letters. For alphanumeric validation, an explicit class such as [A-Za-z0-9] states the intent unambiguously.

Regex match between two characters or delimiters

This is the single most common request, and the naive version is wrong. Writing \[.*\] against text with several bracketed sections matches from the first opening bracket to the last closing one, because the quantifier is greedy.

Make it lazy with \[.*?\] so it stops at the first closing bracket. A stricter and faster alternative is a negated class: \[([^\]]*)\] matches any run of characters that are not a closing bracket, which cannot overshoot and requires no backtracking. Prefer that form whenever the delimiter is a single character.

Anchors and line boundaries

The caret matches the start of the input and the dollar sign matches the end. With the multiline flag they match at every line boundary instead, which is what you want when processing a document line by line and not what you want when validating a single value.

The word boundary \b matches the position between a word character and a non-word character. It is how you match a whole word rather than a fragment: \bcat\b finds the word cat without matching it inside concatenate. It is a zero-width assertion, so it consumes nothing.

Excluding matches

Two constructs cover the usual needs. A negated character class such as [^abc] matches any single character other than those listed, and negative lookahead such as ^(?!admin) rejects a whole string that begins with particular text.

Combining lookahead with an anchor gives a readable exclusion rule: ^(?!.*password).*$ matches any line that does not contain the word anywhere. This is usually clearer than trying to construct a positive pattern for everything you want to allow, which grows unmanageable quickly.

Escaping literal characters

Characters with special meaning must be escaped to match literally: the dot, question mark, plus, asterisk, parentheses, square brackets, braces, caret, dollar sign, pipe, and backslash. A pattern intended to find a literal dot but written as a bare . matches every character instead, which is a silent and very common bug.

Inside a character class the rules relax, since most characters lose their special meaning there. When building a pattern from user input, use the language's escaping helper rather than escaping by hand, because a single missed character turns a search into an injection.

Regex match reference table

The rows apply in every flavour, so a regex match js expression and its Python equivalent differ only in how you invoke them. Where you want a regex match character by character, a single-character class is enough; where you want a regex match any string result, a quantifier is what changes it. A regex match but not include requirement is served by lookahead rather than by a longer pattern.

The table below collects the requests that come up most often. Each row is a starting point rather than a finished validator: anchor it, and adjust the character classes for your data before using it in production.

GoalPatternNote
Regex match any character.Excludes newline unless the dot-all flag is set
Regex match any character including newline[\s\S]Works without a flag in every engine
Regex match any character except a set[^abc]Negated class, matches exactly one character
Regex match anything, or any string.*Greedy; matches empty text too
Regex match any number of characters.{0,}Same as .*, written explicitly
Regex match all characters on a line^.*$Add the multiline flag for line-by-line work
Regex match digits\d+Use [0-9]+ for ASCII digits only
Regex match numbers with a sign-?\d+Anchor it to validate a whole value
Regex match any number, decimal included-?\d+(?:\.\d+)?Add (?:[eE][+-]?\d+)? for exponents
Regex match decimal number only\d+\.\d+Requires a fractional part
Regex match alphanumeric[A-Za-z0-9]+Explicit class avoids the underscore in \w
Regex match whitespace\s+\S+ matches non-whitespace
Regex match dot, literally\.Unescaped, the dot matches everything
Regex match backslash\\Often \\\\ once inside a quoted string
Regex match colon:No escape needed outside a character class
Regex match brackets\[|\]Escape square brackets; parentheses too
Regex match beginning of string^ or \A\A ignores the multiline flag
Regex match beginning of line^ with multilineWithout the flag it matches input start only
Regex match between two characters\[([^\]]*)\]Negated class cannot overshoot
Regex match between two stringsSTART(.*?)ENDLazy quantifier stops at the first END
Regex match contains string^(?=.*word)Lookahead; no need to consume the text
Regex match but exclude, or not include^(?!.*word).*$Negative lookahead rejects the whole line
Regex match but don't capture(?:...)Groups without allocating a capture slot
Regex match capture group(\d{4})-(\d{2})Read results by index, or name them
Regex match case insensitive/pattern/iRegex match case sensitivity is flag-controlled
Regex match a whole word\bword\bWord boundaries consume nothing

Regex match count, check, and replace

Counting occurrences is a global-flag operation rather than a pattern feature. To get a regex match count, run the expression globally and measure the result: (text.match(/p/g) || []).length in JavaScript, len(re.findall(p, s)) in Python, or preg_match_all() in PHP, which returns the count directly.

A regex match check that only answers yes or no should use the boolean method rather than fetching matches, because it stops at the first success. Replacement uses the same pattern with a substitution string, and testing a regex replace before running it over real data is worth the extra minute, since a greedy quantifier in a replacement silently eats more than intended.

Building patterns incrementally

Assemble a complex expression from parts and verify each addition against real text before adding the next. A pattern built in one attempt and tested once usually matches something unintended, and finding out which piece is responsible is far harder after the fact.

Keep a set of inputs that must fail alongside the ones that must pass, and re-run both whenever the pattern changes. Where an engine supports extended mode, whitespace and comments inside the pattern make a long expression readable, which matters more than brevity for anything that will be maintained.

How to test regex in each language

An online regex tester is the fastest way to shape a pattern, but the flavour you deploy to decides the final behaviour. The table below shows the call that performs a regex test and match in each environment, so you can confirm a pattern where it will actually run.

EnvironmentTest a regexNotes
Test regex JavaScript / test regex js/p/.test(s)Beware lastIndex with the global flag
Regex tester Pythonre.search(p, s)match() anchors at the start, search() does not
Test regex Java / regex tester JavaPattern.compile(p).matcher(s).find()matches() requires the whole string
Regex tester C# / regex checker C#Regex.IsMatch(s, p)Same engine as a regex tester .NET uses
Regex tester PHPpreg_match('/p/', $s)Delimiters are part of the pattern string
Check regex bash / bash test regex match[[ $s =~ $re ]]Do not quote the right-hand side
Regex test bash with grepgrep -E 'p'-P for Perl syntax where available
Regex tester PowerShell$s -match $pPopulates the $Matches variable
Regex test Angularnew RegExp(p).test(v)A regex tester Angular form uses in a validator
Regex matcher in Goregexp.MustCompile(p).MatchString(s)RE2 engine, no backtracking

Naming varies as much as syntax. What one team calls regex testing C# another writes as a test regex C# step or a check regex C# helper, and a test regex C# online sandbox exercises the same Regex.IsMatch call. On the shell side, a regex tester bash workflow and a test regex bash script both mean running the expression through [[ =~ ]] or grep -E. To test regex expression behaviour reliably, run it where it will live.

Two differences catch people out when moving a pattern between these. Java and .NET distinguish a full-string match from a search, so a pattern that works in one may appear to fail in the other purely because of the method chosen. And a regex tester C# online or a browser tool cannot reproduce a shell flavour, which is why a check regex bash step belongs in the shell itself.

Regex test cases and backtracking

Keep the inputs you tested. A saved set of regex test cases turns a pattern into something you can change safely, and it is the only practical way to notice that a small edit started accepting text it used to reject.

Include at least one near-miss that fails at the very end of the string, because that is what a regex backtracking tester exists to expose. A pattern with nested quantifiers can run in exponential time on exactly that shape, and a test regex pattern online run against a short sample will never reveal it. If a pattern will process user input, measure it against a long failing string before shipping.

Knowing when to stop using a regex

Some structures look regular and are not. Nested brackets, HTML, and any format with recursive nesting cannot be described by a genuine regular expression, and patterns that appear to handle them work only for the depth you happened to test. Use a real parser for markup, a CSV library for delimited data, and a JSON parser for JSON.

Even where a pattern is technically possible, readability is a fair reason to reject it. An expression that takes several minutes to understand will be misread during the next change, and a short sequence of string operations is often both clearer and faster. The best use of a regular expression is a small, well-tested piece of a larger routine rather than the whole of it.

References: MDN’s RegExp reference documents the JavaScript flavour, and the Python re documentation shows where another common flavour differs.