About the Regex Tester
A regular expression is a small language for describing the shape of text. You write a pattern, an engine walks your input looking for stretches that fit it, and you get back where those stretches were and what parts of them you asked to keep. It is the fastest way to pull dates out of a log file, validate a form field, or rename three hundred files - and the easiest thing in programming to write almost correctly.
That is what a tester is for. Reading a pattern back does not tell you what it matches; running it does. This page runs yours against text you supply, marks every match, lists each capture group by number and by name, previews a replacement, and breaks the pattern into a labelled list so you can see which part is doing what.
Matching happens in your browser, on a background thread. Nothing you paste is uploaded or logged, which matters because the text you want to test a pattern against is often a real log line or a real customer record.
How to use this tool
- Type or paste your pattern into the expression field. Write it without the surrounding slashes - just the pattern itself.
- Tick the flags you need. Global finds every match rather than only the first; ignore case is self-explanatory; multiline changes what the ^ and $ anchors mean.
- Put the text you want to search into the test string box. Results update as you type.
- Read the matches panel for what was found and where, and the breakdown panel beside it for what each piece of the pattern means.
- Optionally type a replacement, using $1 or $<name> to reuse a captured group, and copy the rewritten text.
Greedy, lazy, and why your pattern grabbed too much
By default every quantifier is greedy: it takes as much as it can and gives characters back only when the rest of the pattern cannot otherwise match. This is behind the most common surprise in regular expressions. Run the pattern <.+> against the text <b>bold</b> and you get one match spanning the entire string, not the two tags you expected, because .+ swallowed everything up to the final angle bracket before backing off.
Adding a question mark after the quantifier makes it lazy - it takes as little as possible and grows only when forced. <.+?> against the same input matches <b> and </b> separately. The breakdown panel on this page labels lazy quantifiers explicitly, because a single question mark buried in a long pattern is easy to miss when you are reading someone else's work.
The other frequent surprise is that a dot does not match a line break unless you set the dot-all flag. A pattern that works perfectly on one line and mysteriously fails across two is almost always this.
Catastrophic backtracking, and why this tool uses a background thread
JavaScript's regex engine is a backtracking engine. When a match fails it does not give up; it returns to the last point where it had a choice and tries the other branch. Usually that is cheap. With nested quantifiers it is not, because the number of ways to divide the input between the inner and outer quantifier grows exponentially with its length.
The canonical example is (a+)+$ tested against a string of a characters that ends in something else. Measured while building this tool, on the same JavaScript engine Chrome uses, that pattern against just 31 characters ran for 61 seconds - over a minute of solid computation for an input you could type by hand. Doubling the input roughly doubles the exponent, so 40 characters is hours.
This matters beyond curiosity. A regular expression that runs on user-supplied input in a web server is a denial-of-service vector, and it has a name: ReDoS. A single crafted request can pin a CPU core for minutes. It is worth checking any pattern you plan to run on untrusted input for nested quantifiers before it ships.
It is also why this page evaluates your pattern in a Web Worker rather than on the page's main thread. A regular expression in the middle of backtracking cannot be interrupted - it ignores timers, events, and every other signal, so a tab that starts one simply stops responding until it finishes. Running it on a separate thread means it can be killed. If a pattern here takes longer than one second, the thread is terminated and you get a message instead of a frozen page.
Capture groups: numbered, named, and non-capturing
Round brackets do two jobs at once. They group part of a pattern so a quantifier or an alternation applies to the whole thing, and they capture whatever matched inside them for later use. Groups are numbered from one, left to right, by the position of their opening bracket - which is why adding a bracket early in a pattern silently renumbers everything after it and quietly breaks the replacement string you wrote last week.
Named groups fix that. Writing (?<year>[0-9]{4}) lets you refer to the capture as year rather than as $1, in both the match results and the replacement. The name survives edits elsewhere in the pattern, so it is the better choice in anything you intend to keep.
When you want grouping without capture, use (?: at the start of the group. It keeps the numbering of your real groups stable and tells the next reader that nothing inside is meant to be extracted. This page labels each bracket by kind, so you can see at a glance which groups are capturing and what number each one holds.
What the flags actually change
The global flag is the one with the most confusing effects, because it changes behaviour rather than just breadth. With it, a replacement rewrites every match; without it, only the first, which is the engine's rule and not a limitation of any particular tool. It also means the regex object keeps a position between calls, which is a classic source of bugs when the same regex object is reused in a loop.
Multiline changes the meaning of the anchors: ^ and $ stop meaning start and end of the whole input and start meaning start and end of each line. Sticky goes further and requires the match to begin exactly at the current position rather than searching forward, which is what makes it useful for writing tokenisers.
The unicode flag makes the pattern operate on code points rather than UTF-16 code units, and it is required before \p{...} property escapes such as \p{Lu} will work at all. Without it, that syntax either throws or means something else entirely.
Frequently asked questions
- Which flavour of regular expression does this tester use?
- JavaScript's, exactly as implemented by your browser, because the pattern is handed to the browser's own engine. That makes it authoritative for anything you will run in Node or in a browser, and close but not identical for other languages. PCRE, Python and Go each differ in places - most visibly in named group syntax, lookbehind support and how they treat unicode - so a pattern verified here may need small changes elsewhere.
- Why does my pattern show an error but not point at the position?
- Because JavaScript does not report one. The engine throws a message such as "Unterminated group" or "numbers out of order in {} quantifier", which tells you what is wrong but carries no index into the pattern. This tool shows the engine's message verbatim rather than guessing at a location, since a caret pointing at the wrong character is worse than no caret at all.
- What happened when it said the pattern was stopped after a second?
- The pattern was still running, so the background thread evaluating it was terminated. Almost always the cause is catastrophic backtracking from nested quantifiers - something of the shape (a+)+ or (\d*)*. Simplifying the nesting usually fixes it: an inner quantifier and an outer quantifier over the same characters is the pattern to look for.
- Is there a limit on how many matches it will show?
- It stops at 500 and tells you when it has, which keeps the page responsive on a large paste. Finding matches is fast; rendering thousands of rows in a browser is not. If you are hitting the cap, the useful next step is usually to narrow the pattern rather than to see more of the same.
- Should I validate an email address with a regular expression?
- For a rough client-side check, yes - something that requires an at sign with text either side of it and a dot in the domain catches genuine typos. For real validation, no. The grammar for a legal address in RFC 5322 permits quoted strings, comments and nested brackets, and the regular expressions that implement it faithfully are thousands of characters long and still cannot tell you whether the mailbox exists. Sending a confirmation message is the only test that answers the question you actually care about.
