Regex Tester

Match, highlight, replace and split as you type, with your pattern read back in English

//

Matches

Nothing yet.

Replace, and split

$1 for a numbered group, $<name> for a named one, $& for the whole match, $$ for a literal dollar.

What this pattern says

A regular expression describes a shape, and the engine walks the text looking for it. Everything that surprises people about regular expressions comes from how it walks: left to right, taking as much as it can, and backing up when that turns out to be too much.

Greedy is the default, and it is usually wrong

Text     <b>bold</b> and <i>italic</i>

<.+>     matches  <b>bold</b> and <i>italic</i>     one match, the lot
<.+?>    matches  <b>  </b>  <i>  </i>              four matches
<[^>]+>  matches  <b>  </b>  <i>  </i>              four matches, faster

.+ takes everything it can and only gives characters back when the rest of the pattern fails. Against a line with two tags on it, the first> it settles on is the last one in the line, not the first.

Adding ? makes a quantifier lazy: it takes as little as it can and grows only when forced. That fixes the result. But the better fix is usually the third line, [^>]+, which says what you actually mean, which is any character that is not a closing bracket. That leaves the engine nothing to reconsider.

The pattern that never finishes

Pattern   (a+)+b
Input     aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa      (30 a's, no b)

The engine has to try every way of splitting 30 characters
between the inner + and the outer +, and only then give up.
That is over a billion attempts for thirty characters, and
each further character doubles it.

Fix        a+b
           Same language. One way to match it.

This is catastrophic backtracking, and it is the reason the matching here runs in a Web Worker, not on the page. A running match cannot be interrupted, so the only way to stop one is to destroy the thread it is on. After 1.5 seconds that is what happens, and you get a message instead of a frozen tab.

The shape to watch for is two quantifiers that can both claim the same characters: (a+)+, (a|a)*, (\w+\s*)+, or two .* in the same pattern. Failure is the trigger. On input that matches, the pattern is fine, so it sails into production and then meets one bad request. Cloudflare took a large part of the web offline in July 2019 with exactly this, one .*.*=.* in a firewall rule.

Groups, and the two things they do

(\d{4})-(\d{2})            two numbered groups: $1 and $2
(?<year>\d{4})-(?<mo>\d{2})  the same, named: $<year> and $<mo>
(?:\d{4})-(\d{2})           the first is structural, not captured

\1                          whatever group 1 matched, again:
                            the same text, not the same pattern
(\w)\1                      a doubled letter: ll, ss, oo

A group does two jobs at once: it brackets part of the pattern so a quantifier or an alternation applies to all of it, and it captures whatever matched so you can refer to it afterwards. (?:...) does the first without the second, which is worth using in a pattern with many groups so the numbering stays readable.

A group that did not take part is different from a group that matched nothing. In (a)|(b) against "b", group 1 isundefined and group 2 is "b". The match list above distinguishes the two, because in code the first is a null check and the second is not.

Anchors match positions, not characters

\b   the edge between a word character and anything else
     cat  matches inside "concatenate"
     \bcat\b  does not

^$   the ends of the string, or of each line with the m flag
     "one\ntwo" with ^\w+$ and m  ->  two matches
     "one\ntwo" with ^\w+$ and no m  ->  none

\b, ^, $ and the lookarounds all match a place between characters, not a character. They consume nothing, so\b\b means the same as \b, and a lookahead can be followed by a pattern that matches the very same text again.

The m flag is the one people forget. Without it, $means the end of the whole string, so a pattern anchored with^...$ finds nothing at all in a multi-line block. With it, both anchors apply at every line break.

The flags, and which ones change the answer

FlagWhat changes
gEvery match, not just the first. Also makes the regex object stateful, and that is the source of the "why does .test() alternate true and false" bug
iCase-insensitive. With u, this follows Unicode case folding, so K matches the Kelvin sign
m^ and $ apply at every line break
s. matches a line break too. Without it, . never does
uTreats the pattern as code points, so an emoji is one character and \p{...} works
vEverything u does plus set operations inside brackets. Replaces u; the two cannot both be on
yMatch only at the current position, never later. For tokenisers

The stateful-regex bug

A regular expression with g remembers where it got to, in a property called lastIndex, and test andexec both move it. So a single regex object reused across calls returns true, then false, then true, on identical input.

It bites hardest when the regex is a module-level constant, which is exactly where people put it for performance. Either drop the g when all you want is a yes or no, or build the regex where it is used.

What a dot does not match

Not a line break, unless s is on. And without u, not a whole character either: JavaScript strings are UTF-16, so an emoji is two code units and . matches half of one. Turn uon and it matches the character. That also applies to [^x], quantifiers, and anything counting length.

Where this differs from other engines

This is the JavaScript engine. It is what runs in your browser and in Node. It is close to PCRE but not identical, and the gaps are worth knowing before pasting a pattern from somewhere else: