REGEX: Write a Pattern and See What It Does
A regular expression is a small program most people write by guessing. This one runs your pattern against your text in this tab, shows every match and every capture group, reads the pattern back to you in plain English, previews a replacement and a split, and refuses to hang when a pattern starts backtracking. The pattern and the text stay in your browser.
The pattern
Written the way you would write it in JavaScript, between the slashes. Flags go on the right.
The text to run it against
What it does
$1 a numbered group · $<name> a named group ·
$& the whole match · $` the text before it ·
$' the text after it · $$ a literal dollar sign.
Waiting for a pattern.
Before
After
What the pattern says, in words
Read back from a parser written for this page. Where it cannot read a construct it says so instead of guessing.
Patterns worth keeping
Each one is the practical version, not the complete one, and each says what it misses. Choosing one loads it above with a sample to try it on. A pattern anchored with ^ and $ is loaded with the m flag so every line of the sample is tested on its own; when you use it against a single value, drop the m and the g.
Cheat sheet
The differences that bite
The dot does not match a newline
In JavaScript . means every character except the four line terminators: line feed, carriage return, line separator and paragraph separator. A pattern that works on one line quietly stops at the end of it. The s flag (dotAll) makes the dot match those four as well. [\s\S] is the older way of saying the same thing and still reads more clearly to some people.
\b is defined in terms of \w, and \w is ASCII
\w is exactly [A-Za-z0-9_], so a word boundary sits between caf and é. On café the pattern \bcafé\b fails and \bcaf\b succeeds. The u flag does not change this. For text that is not ASCII, match on properties instead: \p{L} with u for a letter, and (?<!\p{L}) and (?!\p{L}) in place of the boundaries.
Greedy takes everything, lazy takes nothing
<.*> on <a><b> matches the whole string, because * runs to the end and then hands characters back until a > can be found. <.*?> matches <a>. A lazy quantifier is not faster in general; it just fails in the other direction. The fastest of the three is usually neither: <[^>]*> can never overshoot, so it never has to come back.
^ and $ mean the whole string until you say otherwise
Without m they anchor to the start and end of the whole subject, not of each line. With m (multiline) they anchor at every line break as well. JavaScript is stricter than Perl here: without m, $ matches only at the very end and not before a trailing newline, so /^admin$/ rejects "admin\n". Turn m on and it accepts it. That is the trap in a validation pattern: m left switched on means a second line of anything gets through.
The g flag gives the pattern a memory
A RegExp with g or y keeps a lastIndex between calls. Calling .test() twice on the same string with the same object returns true then false. That is not a bug, it is the cursor moving. Build the expression fresh, or reset lastIndex, or use String.matchAll, which does it for you.
y is not g
Sticky matching only succeeds if the match starts exactly at lastIndex. It never scans forward. That makes it the right tool for a tokeniser walking a string, and the wrong tool for finding something somewhere in it.
u changes what a character is
Without u, a pattern works on UTF-16 code units, so an emoji is two characters and . matches half of one. With u it works on code points, \p{...} properties become available, and unknown escapes such as \q become errors instead of meaning a literal q. Turning u on can therefore break a pattern that was silently wrong before.
Case-insensitive is not the same as case-folded
i alone will not match the Kelvin sign against k, and will not fold the Turkish dotless ı the way a Turkish reader expects. With u the folding follows Unicode simple case folding, which is closer but still not the locale-aware answer. Normalise the text before matching when this matters.
Some things are not regular
Nested brackets, HTML, and anything that has to count depth cannot be matched by a regular expression, however clever. JavaScript has no recursion and no atomic groups to soften this. Where a pattern almost works on nested structure, it will be the malformed input that breaks it, which is exactly the input you were trying to catch.
What this is, and what it is not
It is your browser's regular expression engine. The pattern is handed to JavaScript's own RegExp, so what you see here is what the same pattern will do in a browser or in Node. It is not PCRE, not Python's re, not Go's RE2. Atomic groups, possessive quantifiers, recursion and conditionals do not exist in this dialect, and the page says so rather than quietly failing.
The match runs in a worker with a stopwatch on it. Some patterns take longer than the age of the universe on a short string. That is catastrophic backtracking, and in a normal page it locks the tab for good. Here the match runs in a Web Worker with a time budget. If the budget runs out the worker is destroyed, the page stays alive, and you are told which part of the pattern is the likely cause.
The explanation is a reading, not a compiler. The breakdown is produced by a parser written for this page. It handles the syntax people actually write: literals, classes, groups, alternation, quantifiers, anchors, backreferences and lookaround. Where it meets something it cannot read it says so for that part instead of inventing a description. Trust the match list over the prose.
Every pattern in the library carries what it misses. There is no correct email regular expression, no correct URL one, and no correct phone one. The patterns here are the practical ones, each with a line saying what it lets through and what it wrongly rejects. Use them as a first filter, never as a verdict.
Nothing leaves this tab. The pattern, the flags, the subject text and the replacement are held in this page and saved to this browser's local storage so they survive a reload. They are never sent anywhere. Clearing site data for obscuraos.com removes them.
Questions people ask
Why does my pattern only find the first match?
Because it has no g flag. Without g, JavaScript's matching stops at the first result. Tick g in the flags row and the whole subject is scanned. With g the same RegExp object also carries a lastIndex between calls, which is the source of most surprising bugs when a pattern is reused in code.
Why does the dot not match my newline?
Because it never does by default. In JavaScript . means any character except a line terminator. Tick s (dotAll) and it matches newlines too. If you want the opposite, use an explicit class such as [^\n] so the intent is on the page rather than in a flag.
What is the difference between greedy and lazy?
a.*b takes as much as it can and then gives characters back until the pattern fits, so on a1b2b it matches through to the last b. a.*?b takes as little as it can and stops at the first b. Neither is right; they answer different questions. Switch the quantifier in the box above and watch the highlight move.
Is my pattern safe to run on untrusted input?
This page can tell you when a pattern backtracks badly on the subject you gave it, and it names the construct most likely to be responsible. It cannot prove a pattern is safe on every input, because that is undecidable in general. If a pattern runs against text a stranger supplies, prefer one with no nested quantifier over an overlapping class, and put a length limit on the input.
Does it support named groups and lookbehind?
Yes, because your browser does. (?<year>\d{4}) appears in the match list by name as well as by number, and (?<=\$)\d+ works in every current browser. Lookbehind arrived late in Safari, so a very old iOS will throw on it; the error is shown rather than swallowed.
Can I use this on a whole file?
Paste it in. The subject box will hold a few megabytes without complaint, and the match list is capped so a pattern matching a hundred thousand times does not build a hundred thousand rows of markup. The cap is stated when it is reached.