About the Regex Tester
Type a pattern and some text, and this regex tester highlights every match as you type, lists each capture group and previews a find-and-replace. It runs on your browser's own JavaScript RegExp engine, so a pattern that works here behaves the same way in JavaScript, TypeScript and Node.js.
Matching runs in a Web Worker on your device, and nothing you type is uploaded. A pattern that starts backtracking out of control is stopped after 1 second instead of freezing the tab.
- Type your pattern into Regular Expression without the slashes; they're drawn around the field, with the active flags after the closing one. Paste a full literal like
/^\d+$/gminto the empty field and it's split into pattern and flags for you. - Toggle flags with the buttons under Flags.
gis on when the page opens.uandvswitch each other off, andvonly appears in browsers that support it. - Paste the text to search into Test Text. Results highlights each match in alternating colors and the badge counts them. A zero-length match, like the ones
a*finds between other letters, shows as a thin yellow bar. - If the pattern can't compile, Invalid Regular Expression shows the engine's message, such as
Unterminated groupfor a missing). Pattern Too Slow means the run passed 1 second. - Match Details lists every match with its Index and Groups:
$1,$2for numbered groups,<name>for named ones, and(no match)for a group that took no part. Withdon, Index shows start and end offsets. Up to 1,000 matches are listed. - Type a substitution into Replace With to see Replace Result.
$1,$<name>and$&(the whole match) work as inString.prototype.replace. Need a starting point? Common Patterns loads Email, URL, Phone (US), Date (YYYY-MM-DD) or Strong Password with sample text.
InputPattern: ^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])$
Flags: gm
Text:
2024-03-15
2024-13-01
1999-12-31Output2 matches
#1 index 0 2024-03-15
$1: "2024" $2: "03" $3: "15"
<year>: "2024" <month>: "03" <day>: "15"
#2 index 22 1999-12-31
$1: "1999" $2: "12" $3: "31"
<year>: "1999" <month>: "12" <day>: "31"Month 13 fails. But a pattern checks the shape of a date, not the calendar, so 2024-02-31 would pass. Parse the date in code if it has to exist.
InputPattern: (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
Flags: g
Text: Dates: 2024-03-15 and 1999-12-31
Replace With: $<day>/$<month>/$<year>OutputDates: 15/03/2024 and 31/12/1999$3.$2.$1 gives 15.03.2024, the dotted style used in much of Europe.
InputText: José, Zoë and Łukasz met in Kraków and 東京.
Pattern A: \w+ Flags: g
Pattern B: \p{L}+ Flags: guOutputA, 9 matches: Jos | Zo | and | ukasz | met | in | Krak | w | and
B, 9 matches: José | Zoë | and | Łukasz | met | in | Kraków | and | 東京JavaScript's \w is ASCII only, even with u. Python 3's \w is Unicode, so the same pattern finds whole names there.
InputText: <b>bold</b> and <i>italic</i>
Pattern A: <.+>
Pattern B: <.+?>
Flags: gOutputA, 1 match: "<b>bold</b> and <i>italic</i>"
B, 4 matches: "<b>" | "</b>" | "<i>" | "</i>"+ takes as much as it can and backs off to the last >. +? stops at the first > that completes a match.
Regex cheat sheet (JavaScript syntax)
Most of these tokens mean the same in PCRE, Python and Java. The differences come further down.
.any character except a line break (withs, line breaks too).\.is a literal dot.\da digit 0 to 9,\wa letter, digit or underscore (ASCII only),\swhitespace including line breaks.\D,\Wand\Smatch everything else.[abc]one of a, b or c.[^abc]anything but those.[a-z0-9]ranges. Inside brackets,.and+are plain characters.^and$the start and end of the text, or of each line withm.\ba word boundary:\bcat\bfinds cat but not category.*zero or more,+one or more,?optional,{3}exactly three,{2,5}two to five,{2,}two or more. Add?for the lazy version:+?,{2,5}?.(...)a capture group ($1),(?:...)a group that doesn't capture,(?<name>...)a named group.\1or\k<name>repeats the captured text:\b(\w+)\s+\1\bfindsis isandthe theinThis is is a test of the the tool.a|beither side. Group it to limit its reach:^(?:cat|dog)$, not^cat|dog$.(?=...)followed by,(?!...)not followed by,(?<=...)preceded by,(?<!...)not preceded by. They check text without including it:\d+(?=px)returns100from100px.\p{L}a letter in any script,\p{Lu}an upper-case letter,\p{N}any number (Arabic-Indic٣٤٥too),\p{Script=Arabic}one script. These needuorv.
JavaScript regex flags
In code, flags follow the closing slash (/abc/gi) or go in the second argument of new RegExp("abc", "gi").
gglobal: every match, not just the first.matchAllandreplaceAllthrow a TypeError without it.iignoreCase:/colou?r/gifindsColorandCOLOUR.mmultiline:^and$work per line./^\d+$/gfinds nothing in three lines of numbers; withgmit finds all three.sdotAll:.matches line breaks too.uunicode: reads code points, enables\p{...}and rejects sloppy escapes.\qmatchesqwithoutuand fails withInvalid escapewith it.vunicodeSets:uplus set operations.[\p{L}--[a-z]]+(letters except a to z) findsÄÖüandΩinabcÄÖü xyz Ω. Chrome 112, Firefox 116, Safari 17, Node.js 20.ysticky: matches only atlastIndex, which is useful in tokenizers.dhasIndices: start and end of the match and of each group. On10:02 ERROR PAY-402 card declined,(?<level>ERROR|WARN) (?<code>[A-Z]+-\d+)shows Index6-19and groups[6-11]and[12-19]. Chrome 90, Firefox 88, Safari 15, Node.js 16.
Copying a pattern from Python, PHP, Java or Go
Patterns from other languages usually work here. When one doesn't, it's normally one of these:
- Python
rewrites named groups as(?P<name>...), which JavaScript rejects withInvalid group. Its\dand\ware Unicode-aware, so\dmatches٣٤٥, and its lookbehind must have a fixed length. - PHP (PCRE2) and Java have possessive quantifiers (
a++) and atomic groups ((?>...)). JavaScript has neither:a++fails withNothing to repeat. - An inline
(?i)at the start is an error in JavaScript. Use theibutton, or the scoped(?i:abc), which recent Chrome, Firefox and Node.js versions accept. - Go's
regexp(RE2) has no lookarounds or backreferences, but it always runs in linear time. - Java string literals double every backslash, so
\d+becomes"\\d+".
Catastrophic backtracking (ReDoS)
When part of a pattern fails, JavaScript's engine steps back and tries another way to split the text. With nested quantifiers such as (a+)+$, a run of as followed by ! can be split in an exponential number of ways. In fresh Node.js 25 processes, 26 as took 3.8 seconds, 27 took 7.3 and 28 took 15: each extra character doubles the time.
This page stops the run at 1 second. A Node.js server has no such limit, so one long form field can block it for every user. That attack is called ReDoS. To avoid it:
- Don't nest quantifiers over the same characters.
a+$matches the same strings as(a+)+$in microseconds. - Don't let alternatives overlap.
(\w|\d)+is slow for the same reason;\w+is enough. - Limit input length before matching.
- On a server, a linear-time engine (Go's
regexp, or there2package for Node.js) removes the risk, minus lookarounds and backreferences.
Use the pattern in code
The date pattern from the examples in four languages, plus grep. PHP can't use group names in a replacement, so it uses numbers. For JSON, use a parser instead of a regex: the JSON Formatter shows the line and column where it breaks.
const text = "Dates: 2024-03-15 and 1999-12-31";
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
for (const m of text.matchAll(re)) {
console.log(m.index, m[0], m.groups.year);
}
// 7 2024-03-15 2024
// 22 1999-12-31 1999
text.replaceAll(re, "$<day>/$<month>/$<year>");
// "Dates: 15/03/2024 and 31/12/1999"import re
text = "Dates: 2024-03-15 and 1999-12-31"
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
for m in re.finditer(pattern, text):
print(m.start(), m.group(0), m.group("year"))
# 7 2024-03-15 2024
# 22 1999-12-31 1999
re.sub(pattern, r"\g<day>/\g<month>/\g<year>", text)
# 'Dates: 15/03/2024 and 31/12/1999'$text = "Dates: 2024-03-15 and 1999-12-31";
$pattern = '/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/';
preg_match_all($pattern, $text, $m, PREG_SET_ORDER);
foreach ($m as $match) {
echo $match[0], ' ', $match['year'], "\n";
}
// 2024-03-15 2024
// 1999-12-31 1999
echo preg_replace($pattern, '$3/$2/$1', $text);
// Dates: 15/03/2024 and 31/12/1999re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
text := "Dates: 2024-03-15 and 1999-12-31"
fmt.Println(re.FindAllString(text, -1))
fmt.Println(re.ReplaceAllString(text, "${day}/${month}/${year}"))# POSIX extended syntax: [0-9] instead of \d, no lookarounds
grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' app.log
# Perl-compatible syntax (GNU grep; the grep that ships with macOS has no -P)
grep -oP '(?<=\$)\d+' app.logWhich regex flavor does this tester use?
JavaScript (ECMAScript), through your browser's built-in RegExp, so results match match, replace and test in your code. PCRE, Python and Java patterns mostly work too, apart from features JavaScript lacks, such as possessive quantifiers and Python's (?P<name>...).
Why does my regex only find the first match?
The g flag is off. Without it the engine stops at the first match and replace changes only that one.
How do I match a dot, a bracket or another special character?
Put a backslash in front: \. for a dot, \( for a bracket. The characters that need it are . * + ? ^ $ { } ( ) | [ ] \ /. For user input, RegExp.escape(text) does it for you in current browsers. To share a pattern in a link, URL encode it first.
Can a regex validate an email address?
Only roughly. The Email pattern in Common Patterns accepts a..b@example.com, which is invalid, and rejects "john doe"@example.com (allowed by RFC 5322) and jürgen@example.de (allowed by internationalized email, RFC 6531). Use <input type="email"> for a first check and confirm the address by sending a message to it.
What is the difference between greedy and lazy matching?
Greedy quantifiers (*, +, {2,5}) take as much text as they can; lazy ones (*?, +?) take as little as possible. On <b>bold</b>, <.+> matches the whole string and <.+?> only <b>.
Why does a pattern from Python or PHP fail here?
It probably uses syntax JavaScript doesn't have. (?P<name>...), (?>...) and a leading (?i) fail with Invalid group in Chrome and Node.js, and a++ with Nothing to repeat. Rename groups to (?<name>...), use the flag buttons, and replace possessive quantifiers with plain ones.