Skip to content
cliplimeClipLime

Your regex works on three examples and fails on the fourth. Here is how to find out why

Read a pattern piece by piece, set the right flags, preview a replacement and check the usual mistakes behind a pattern that does not match.

6 min readUpdated October 27, 2026

Skip the readingRegex Tester: Test a JavaScript regex with flags, see matches and groups, and preview a replacement.

The quick answer

Say you wrote ^\d{5}(-\d{4})?$ to check US ZIP codes. It accepts 90210, 10001-1234 and 02134, then rejects the fourth, 94105 , because of a trailing space you cannot see. Put all four on separate lines in the regex tester with the g and m flags on, and the one line that is not highlighted is the failing one.

  1. Type the pattern between the slashes and switch on the flags you need. An invalid pattern shows an error message at once.
  2. Paste the text, including the example that fails. Matches are highlighted as you type.
  3. Read the match list. Each match shows its position and every numbered or named group, and a group that did not take part is labeled did not match.
  4. Make sure Preview a replacement is on, as it is when the page opens, and write a template such as $2 $1 to see the rewritten text before it goes into code.
Regex TesterType a pattern, paste your text and see every match, group and replacement live.Open it

The fix is to allow white space before the end, ^\d{5}(-\d{4})?\s*$, or to trim the input first. The tester uses JavaScript regular expressions, so it behaves like the engine in your browser. Other languages differ in places, covered below.

Read a pattern one piece at a time

Take \b\d{5}(?:-\d{4})?\b, a pattern for finding ZIP codes inside text:

PieceWhat it does
\bA word boundary: a position between a word character and anything else. It uses up no text
\d{5}Exactly five digits
(?: ... )A group that does not capture
-\d{4}A hyphen and four digits
? after the groupThe whole group is optional
\bAnother boundary, so the digits cannot continue

With the g flag it finds 90210 and 10001-1234 in Ship to 90210 or 10001-1234, not 1234567. and skips the seven digit number, because no boundary falls after its fifth digit. Use ^ and $ to check a whole value, \b to find something inside text.

The flags the tester supports

FlagNameUse it when
gglobalYou want every match, not only the first
iignore casehello should also match Hello
mmultiline^ and $ should match at every line, not only at the ends of the whole text
sdot allThe dot should match line breaks too
uunicodeYou need \p{L} for letters in any language, or emoji to count as one character instead of two
vunicode setsLike u, with set notation inside classes. It needs a recent browser and cannot be combined with u
ystickyA match must start exactly where the search starts
dindicesYou want the position of each group. The tester uses it for the group offsets

Groups and replacements

Parentheses capture part of a match. ^(\w+), (\w+)$ on Lovelace, Ada captures Lovelace as group 1 and Ada as group 2, and the replacement $2 $1 gives Ada Lovelace. Names make this readable: (?<last>\w+), (?<first>\w+) with $<first> $<last>. $& is the whole match and $$ is a literal dollar sign.

For a list with one name per line, use the flags gm. Without m, ^ and $ mean the start and end of the whole text, and a two line list is left unchanged.

To apply a pattern to a whole document, find and replace text with Regex on uses the same pattern syntax (it always has the g and m flags on and ignores case unless Match case is on), runs several rules in order and can step through each match.

The classic mistakes

MistakeWhat happensFix
Unescaped dotv1.2 also matches v1x2, because . matches almost anythingWrite v1\.2. Inside a class the dot is literal: [.]
Greedy repeat<.+> on <b>bold</b> matches the whole stringUse <.+?> or <[^>]+> to get <b> and </b>
Anchors^\d{5}$ fails on 94105 . In JavaScript $ means the very end, not before a final line breakAllow \s* before $, or trim the input
Multiline text^ and $ match only at the ends of the whole text, and . stops at a line breakAdd m for lines, s for the dot, or use [\s\S]
Accented letters\w+ on café matches only caf, because \w covers only A to Z, a to z, digits and the underscoreUse \p{L}+ with the u flag
Runaway backtracking(a+)+$ on thirty a's followed by b can split the a's into groups in 2^29 = 536,870,912 waysAvoid a repeat inside a repeat over the same characters, anchor the pattern, limit the text length

The last row is catastrophic backtracking. On a server it can take a service down, and in a web page it freezes the tab. Where the browser supports workers, the tester runs each pattern in a separate worker and stops it after 2 seconds with an explanation, so you can experiment safely. Your pattern and text stay in your browser, which matters when the sample is a log with real addresses or tokens, and the Network tab will confirm that no request carries them.

It works here but fails in my code

Start with your string literals. In JavaScript, new RegExp("\d+") is really /d+/, because the string lost its backslash. Write "\\d+" or use a literal such as /\d+/. Then compare flags: your code may lack the g, i or m you had on.

If the code is not JavaScript, expect differences. These are the ones to check first:

  • A pattern that starts with (?i) is a syntax error in JavaScript. Set the i flag instead.
  • Python writes named groups as (?P<name>...), which JavaScript rejects. JavaScript writes (?<name>...).
  • JavaScript has no atomic groups (?>...) and no possessive quantifiers such as a++. Both are errors.
  • \A and \Z do not mean start and end of text in JavaScript. Without the u flag they just match the letters A and Z. Use ^ and $.
  • Python's $ also matches just before a final line break, and in Python 3 \w on text also matches letters of other scripts. JavaScript's $ and \w do neither.

That list is not complete, so test in the engine you will actually use.

A small cheat sheet

TokenMatches
.Any character except a line break
\d \w \sA digit, a word character (A to Z, a to z, 0 to 9, underscore), white space
\D \W \SThe opposite of each
[abc] [a-z] [^abc]One of these, a range, anything except these
^ $ \bStart, end, word boundary
* + ?Zero or more, one or more, zero or one
{3} {3,} {3,5}Exactly 3, at least 3, between 3 and 5
(abc) (?:abc) (?<n>abc)Capture group, group without capture, named group
a|bEither a or b
(?=x) (?!x) (?<=x) (?<!x)Followed by, not followed by, preceded by, not preceded by
$1 $<n> $& $$In a replacement: group 1, a named group, the whole match, a dollar sign

Where a regex is the wrong tool

If you only want the emails, links, numbers, hashtags or phone-like numbers in a block of text, the text extractor finds them with no pattern to write and removes duplicates. A pattern's list of hits often repeats, and the duplicate line remover keeps the first or last copy of each line.

Do not parse JSON with a regex: format and validate the JSON instead. CSV fields can hold commas inside quotes, which a simple pattern splits wrongly, so use a proper CSV conversion instead. After a replacement, compare the before and after to see exactly what changed.

Find and Replace TextApply a regex to a whole document, with several rules in order and a step through mode.Open it

Try it yourself

Questions people ask

Why is my regex not matching?

Check an unescaped dot or bracket, an anchor that demands the whole text, a missing flag (g for every match, i for case, m for lines) and invisible characters such as a trailing space. Paste the failing example into the regex tester and see what is highlighted.

What does the g flag do?

The global flag makes the search continue after the first match so that every match is found. Without it a regex stops at the first match, and the tester says so when that is why you see only one. In code you set the flag on the pattern, for example /cat/g.

How do I match a literal dot, bracket or dollar sign?

Put a backslash in front: \., \[, \$. Inside a character class most symbols are literal already, so [.] matches a full stop. In a replacement template, write $$ for a literal dollar sign. In a string literal in code, double each backslash as well.

What is catastrophic backtracking?

It happens when nested or overlapping repeats can match the same text in a huge number of ways, so a failing match takes exponential time. The tester stops such a pattern after 2 seconds and says so. Avoid a repeat inside a repeat.

Will a pattern from the tester work in Python or another language?

Often, but not always. The tester follows JavaScript rules, and languages differ in named group syntax, inline flags such as (?i), atomic groups, \A and \Z, and what $ and \w match. Re-test the pattern in the engine you will really use, with the same flags.

How do I match only whole words?

Wrap the word in word boundaries: \bcat\b matches cat but not concat or cats, where a bare cat would match inside both. The find and replace tool also has a Whole word option, so you do not need to write the boundaries yourself.

All Data and developer guides Browse all data & developer tools