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.
- Type the pattern between the slashes and switch on the flags you need. An invalid pattern shows an error message at once.
- Paste the text, including the example that fails. Matches are highlighted as you type.
- 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.
- Make sure Preview a replacement is on, as it is when the page opens, and write a template such as
$2 $1to see the rewritten text before it goes into code.
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:
| Piece | What it does |
|---|---|
\b | A 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 group | The whole group is optional |
\b | Another 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
| Flag | Name | Use it when |
|---|---|---|
g | global | You want every match, not only the first |
i | ignore case | hello should also match Hello |
m | multiline | ^ and $ should match at every line, not only at the ends of the whole text |
s | dot all | The dot should match line breaks too |
u | unicode | You need \p{L} for letters in any language, or emoji to count as one character instead of two |
v | unicode sets | Like u, with set notation inside classes. It needs a recent browser and cannot be combined with u |
y | sticky | A match must start exactly where the search starts |
d | indices | You 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
| Mistake | What happens | Fix |
|---|---|---|
| Unescaped dot | v1.2 also matches v1x2, because . matches almost anything | Write v1\.2. Inside a class the dot is literal: [.] |
| Greedy repeat | <.+> on <b>bold</b> matches the whole string | Use <.+?> or <[^>]+> to get <b> and </b> |
| Anchors | ^\d{5}$ fails on 94105 . In JavaScript $ means the very end, not before a final line break | Allow \s* before $, or trim the input |
| Multiline text | ^ and $ match only at the ends of the whole text, and . stops at a line break | Add 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 underscore | Use \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 ways | Avoid 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 theiflag instead. - Python writes named groups as
(?P<name>...), which JavaScript rejects. JavaScript writes(?<name>...). - JavaScript has no atomic groups
(?>...)and no possessive quantifiers such asa++. Both are errors. \Aand\Zdo not mean start and end of text in JavaScript. Without theuflag they just match the letters A and Z. Use^and$.- Python's
$also matches just before a final line break, and in Python 3\won text also matches letters of other scripts. JavaScript's$and\wdo neither.
That list is not complete, so test in the engine you will actually use.
A small cheat sheet
| Token | Matches |
|---|---|
. | Any character except a line break |
\d \w \s | A digit, a word character (A to Z, a to z, 0 to 9, underscore), white space |
\D \W \S | The opposite of each |
[abc] [a-z] [^abc] | One of these, a range, anything except these |
^ $ \b | Start, 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|b | Either 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