Test a regular expression
Type an expression and paste the text to run it over. Matches are marked in place and listed beside the text, a replacement shows the text it would produce, and all of it stays in this tab.
How to use it#
- Type your regular expression into the expression box, or pick a common one, such as an email address or an ISO date, from Start from at the top of the settings column. Picking one replaces the expression and sets any flag it needs.
- Tick the flags you need. Global is on by default, so you get every match rather than the first. Unicode and unicode sets cannot be combined, so ticking one unticks the other.
- Paste the text to test into the Test text box.
- To replace, type what each match should become. $1 or $<name> puts a capture group in, and $& puts in the whole match.
- Read the replaced text under the replacement, then the Matches list in the settings column, with the position and capture groups of each, and where each group starts and ends when the indices flag is on. Copy there takes the matched text, one match per line, and Copy result takes the replaced text.
The flags, and which one you are missing#
Global is the flag people forget. Without it an expression stops at the first match, which is correct behaviour and looks like a broken tool when you expected a list. It is on by default here for that reason, and you can turn it off to see what a single match returns.
Ignore case does what it says. Multiline changes what the anchors mean: by default ^ and $ match only at the start and end of the whole text, and with multiline they match at every line break, which is what you want when testing against a log.
Dotall is the other quiet one. A dot matches any character except a line break, so a pattern meant to span two lines silently fails until dotall is on. Unicode changes how the pattern is read, and is needed for property escapes such as \p{Script=Han}. Sticky anchors each match to the position the last one ended at.
Unicode sets, the v flag, is unicode with more allowed inside square brackets: [\p{L}--[a-z]] takes every letter except a to z, and && keeps only what two classes share. The engine refuses u and v in one expression, so ticking one unticks the other. It also reads a class more strictly, and a hyphen, bracket, or slash inside one has to be escaped. Indices, the d flag, changes nothing about what matches. It records where each group starts and ends, and the Matches list shows those positions beside each group.
Capture groups and what no match means#
Every pair of brackets in your pattern captures what it matched, numbered from one in the order the opening brackets appear. Give a group a name, with (?<year>\d{4}), and it is listed by that name instead, which is worth doing the moment you have more than two. A pattern that mixes the two has every group listed, a named one by its name and the rest by number. A named group still takes a number, so in (?<year>\d{4})-(\d{2}) the second group is listed as 2, which is also what $2 refers to in a replacement.
A group that took no part in the match is listed as no match rather than as an empty string, and the difference matters. In (a)|(b) matched against b, the first group did not participate at all, while a group like (x?) genuinely matched nothing. JavaScript reports the first as undefined and the second as an empty string, and many other languages use null for the first.
Replacing, and what the dollar signs mean#
The replace box works like JavaScript's own replace. Each match is swapped for what you type, and the text around the matches is kept as it is, so (\d+) replaced with "#$1" turns "order 42 shipped" into "order #42 shipped", and matching the whole text, as .*?(\d+).* does, gives just "#42". A few dollar sequences stand for part of the match. $1 to $99 are the numbered groups, $<year> is a named one, $& is the whole match, $' is the text after it, and a dollar sign followed by a backtick is the text before it. $$ is a literal dollar sign. A reference to a group your pattern does not have is left in as typed, so $3 in a pattern with two groups comes out as $3.
The flags still apply. Without global only the first match is replaced, which catches people out the same way it does in code. Leave the box empty and the result shows your text with every match removed, which is a quick way to strip whitespace or tags. The replaced text is exactly what replace would return in your own JavaScript, and Copy result takes it as text.
Other languages spell this differently. Python writes a group in a replacement as \1 or \g<name>, and sed and many editors use \1 as well. Here a backslash has no special meaning in the replacement, so those come out exactly as typed.
Patterns people start from#
Each of these is in the Start from menu, which fills the expression box and ticks or unticks a flag where the pattern needs it. For an email address, \b[\w.+\-]+@[\w\-]+(?:\.[\w\-]+)+\b finds the usual shape in a block of text. It checks the shape, not whether the mailbox exists, and a pattern that accepts every legal address is far longer than anyone should maintain, so a confirmation message is the real test. For a web address, https?://[^\s<>]+ runs to the next space. For numbers, -?\d+(?:\.\d+)? takes whole numbers and decimals with an optional minus sign. The (?: ) brackets group without capturing, so the Matches list shows no group for them.
A date such as 2026-10-01 can be pulled apart with (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}), and the replacement $<day>/$<month>/$<year> turns it into 01/10/2026. To trim the spaces and blank lines from the start and end of a block of text, use ^\s+|\s+$ with global on, multiline off, and an empty replacement. With multiline on, \s also takes the line breaks, and the lines run together, which is why the menu turns multiline off for this one. To strip HTML tags from a snippet, <[^>]*> with an empty replacement does it, though it is no substitute for a real parser on anything messy. To collapse runs of spaces, match \x20{2,} and replace with a single space.
The menu also has a phone number, an IPv4 address, and a hex colour. The phone pattern accepts the common shapes, such as (555) 123-4567 and +44 20 7946 0958, and phone numbers vary too much by country for any one pattern to be strict, so treat a match as a likely number rather than a valid one. The IPv4 pattern checks that each part is 0 to 255, so 256.1.1.1 is left out. The hex colour pattern takes three, four, six, or eight digits after the #, the lengths CSS accepts.
Why a pattern can hang a browser#
A regular expression engine of the kind JavaScript uses works by trying possibilities and going back when they fail. For most patterns that is fast. For a pattern with one quantifier inside another, such as (a+)+b, the number of ways to divide the text between the two quantifiers doubles with every extra character, so a subject of thirty characters can take longer than the rest of your day.
This is not a rare curiosity. It is a documented way to take a service down, because a search box that passes user input to a regular expression can be handed a subject that never finishes. Rewriting the inner quantifier, anchoring the pattern, or matching a specific character class instead of a broad one usually fixes it.
Here the matching runs on a background thread rather than the one drawing the page. If an expression has not finished within a second, that thread is destroyed and the page tells you, which is the only reliable way to stop a running regular expression. Your pattern and your text are untouched, so you can edit and try again.
The dialect this tests#
These are JavaScript regular expressions, run by the engine already in your browser, so what matches here is what will match in your own JavaScript. Node.js runs the same engine as Chrome, and TypeScript hands a regular expression to JavaScript unchanged, so a pattern tested here behaves the same in both. Lookahead, lookbehind, named groups, and Unicode property escapes are all supported by current browsers.
Other languages differ in the details. PCRE, used by PHP and close to what Perl does, supports possessive quantifiers and atomic groups that JavaScript has no syntax for. Python spells named groups (?P<name>...). Go uses RE2, which has no backtracking at all, and so rejects lookahead and lookbehind outright while guaranteeing it never hangs. A pattern that works here is worth checking in the language you will ship it in. To read what a pattern means before you test it, paste it into the regex explainer.
Frequently asked questions#
Is my expression or my test text uploaded anywhere?
#
No. The matching runs in this tab, on a background thread inside your own browser. Nothing is sent to a server, so a log extract or a list of real email addresses can be used as test text safely.
Why did my expression get stopped for taking too long?
#
It was still running after a second, which almost always means catastrophic backtracking: a quantifier inside another quantifier, such as (a+)+ or (\s*)*, gives the engine an exploding number of ways to split the text. Because a running expression cannot be interrupted, the background thread is destroyed instead. Simplify the inner quantifier and try again.
Why am I only getting one match?
#
The global flag is off. Without it an expression returns the first match and stops, which is how the language behaves rather than a limitation here. Tick g to get the full list.
What does no match mean next to a capture group?
#
That group took no part in the match at all, which happens when it sits in an alternative the engine did not use. It is different from a group that matched an empty string. JavaScript reports the first as undefined and the second as an empty string, and many other languages use null for the first.
Which flavour of regular expression is this?
#
JavaScript, run by your browser engine. It covers lookahead, lookbehind, named groups, and Unicode property escapes. PCRE, Python, and Go each differ in syntax or in what they allow, so check a pattern in its destination language before relying on it.
How do I use a capture group in the replacement?
#
Put a dollar sign before its number: with (\w+) (\w+) matched against Ada Lovelace, the replacement $2, $1 gives Lovelace, Ada. A named group is $<name>, and $& inserts the whole match. Turn on the global flag to replace every match rather than the first.
Why does ticking v untick u?
#
The engine refuses an expression with both, and there is no reason to want both: v includes everything u does and adds set operations inside square brackets. Ticking one unticks the other so the expression stays valid. A pattern written for u may need a hyphen or bracket inside a class escaped before it works under v.
Can I test a Python, PHP, or Java regex here?
#
Only if it is written in syntax JavaScript reads. Simple patterns with classes, quantifiers, groups, and lookahead behave the same in all of them, but (?P<name>…) from Python, possessive quantifiers from PCRE, and a flag such as (?i) at the start of a pattern are errors here. Tick the flags yourself instead of writing them into the pattern, and check the result in the language you will run it in.
What is the d flag for?
#
It makes the engine record where each capture group starts and ends, not only what it matched. It does not change what matches. With it ticked, the Matches list shows a start and end position beside each group, which helps when you need to highlight or slice the text a group captured.
Why does it list only the first 1000 matches?
#
A longer list is slower to draw than it is useful to read. When the limit is hit, the Matches figure reads 1,000, first only, rather than a true count, so if you are counting rather than reading, narrow the text or the pattern.