Test a pattern and see exactly what it catches
Enter a regular expression and a sample text, and this tester runs the pattern in the JavaScript engine (the one your browser, Node and most web editors share), lists every match with its position and capture groups, highlights the matches in place, and previews a replacement. The engine is named deliberately: regex dialects differ between languages, and a pattern that works here may need adjusting for Python, PCRE or .NET, which the notes below flag where it matters.
Pattern
Test text
Replacement (optional)
Disclaimer: This calculator gives estimates for general information only. It is not financial, medical or other professional advice. Patterns run in your own browser with match and length caps; a pathological pattern can still make the tab work hard for a moment, as it would in any regex engine.
What this calculator does
It executes a regular expression honestly and reports completely: every match with its character position, the whole match and each capture group (numbered and, where the pattern names them, named), a highlighted rendering of the text so matches can be seen in context rather than merely counted, and a replacement preview when a replacement string is supplied. Invalid patterns are reported with the engine’s own error, which names the fault (an unterminated class, an unbalanced parenthesis) closely enough to fix it. Safety limits are part of the tool rather than fine print: the text is capped, the match list is capped at 1,000 entries so a global zero-width pattern cannot generate an endless report, and zero-width matches are advanced through manually, the standard technique, so a pattern like \b terminates and reports its four boundaries instead of looping forever.
How to use it
- Build patterns against real, messy text. Test on samples that include the awkward cases (empty lines, the address with a subdomain, the name with an apostrophe), not just the tidy example the pattern was written from. Most regex bugs are not syntax errors; they are matches and non-matches nobody looked at, and the highlight view exists to make looking effortless.
- Read the group columns, not just the match. A pattern can match the right text with the wrong groups (the parentheses one position off), which only shows when the replacement is applied. Checking group 1 and group 2 here, then reading the replacement preview end to end, catches the class of bug that passes every “it matched” test and corrupts data in production.
- Toggle flags one at a time. Flags change meaning silently: adding m redefines every ^ and $ in the pattern, and i widens every class. When a result surprises you, the flag panel is the first suspect, and testing with and without the flag isolates it faster than staring at the pattern.
- Zero-width matches are positions, not text. Assertions like \b, ^ and lookaheads match places between characters. They appear in the table with their index and an empty match, and are not highlighted (there is nothing to highlight), which the results note. A count of them is a count of places, and reading it as a count of words or lines is the standard misreading.
- Name the engine when you share a result. This tester uses JavaScript’s dialect: no possessive quantifiers, no \K, lookbehind supported, named groups as (?<name>…). Python spells named groups (?P<name>…), PCRE differs elsewhere. A pattern verified here is verified for JavaScript and close cousins, and the notes on this page mark the differences that bite most often.
How it is calculated
- The pattern and flags are compiled by the JavaScript RegExp engine; an invalid pattern throws, and the engine’s message is shown verbatim. With g or y, matches are enumerated by repeated execution from the last index, with a manual one-character advance after any zero-width match; without them, the first match only is reported. Groups are read from each match in order, named groups from the match’s groups object. The highlight is built by escaping the text and wrapping match spans; the replacement preview applies the engine’s own replace with your replacement string and flags.
- Limits: text 50,000 characters; at most 1,000 matches listed (the true count is reported if the cap is hit, with the list truncated and labelled).
Worked examples
Extracting the parts of email addresses. Pattern \b(\w+)@(\w+)\.com\b with flag g, against “Contact ana@example.com or bob@sample.com today.” Matches found: 2. Match 1 is “ana@example.com” at index 8, group 1 “ana”, group 2 “example”. Match 2 is “bob@sample.com” at index 27, groups “bob” and “sample”. With replacement $1 at $2, the preview reads “Contact ana at example or bob at sample today.”, which verifies the groups as well as the match, the whole point of testing them together. (The pattern is a teaching example: real email validation is famously not a regex job, as the limitations say.)
Line anchors with the multiline flag. Pattern ^(?<word>[A-Z]+)$ with flags g and m, against three lines “abc”, “DEF”, “ghi”: one match, “DEF” at index 4, with the named group word = “DEF”. Without m, the same pattern finds nothing, because ^ would mean the start of the whole text: a complete, silent change of meaning from one checkbox, demonstrated by toggling it.
A zero-width pattern. Pattern \b with g against “hi there” reports 4 matches at indexes 0, 2, 3 and 8 (the boundaries at both ends of both words), each with an empty match text. Any tester that hangs or reports thousands on this input has a broken zero-width loop; this one advances explicitly and terminates.
Reading your results
Start with the count and ask whether it is the count you predicted: off-by-one counts usually mean a missing g flag, an anchor behaving under the wrong m setting, or a greedy quantifier swallowing a separator (a match spanning what should have been two matches). Then scan the highlight for near misses, text you expected to match that stayed plain, because false negatives hide there while the table only parades the successes. Finally, proofread the replacement preview as prose or data, character by character on at least the first few matches, since replacements are where a correct pattern produces incorrect output quietly.
Limitations
- One engine, named. JavaScript semantics throughout. Patterns relying on other dialects’ features (PCRE’s \K or possessive quantifiers, Python’s conditional constructs) will error or behave differently here, and a pattern this page accepts may still need translation for its target engine.
- Matching is not validating. The email example works on tidy addresses and fails the real world’s edge cases, because full address validation is beyond any sane pattern. Use regex to extract and roughly screen; use parsers and confirmation messages to validate.
- Catastrophic patterns are capped, not cured. Nested quantifiers on near-matching text (the classic (a+)+$ shape) can make any backtracking engine, this one included, work extremely hard. The caps stop the report, not the computation; if the tab pauses on a pattern, the pattern, not the tester, is the finding.
- Huge inputs are trimmed at the door. Fifty thousand characters covers realistic testing. Production-scale validation belongs in your own environment, with this page used to develop the pattern on samples first.
Frequently asked questions
Why does my pattern match only once?
Almost always the g flag. Without it, the engine stops at the first match by design, which is why g is pre-checked here. The second cause is a greedy span: a pattern like “.+” matches the entire text as one match where several were expected, and the fix is a narrower class or a lazy quantifier (“.+?”). The count, the match lengths and the highlight together distinguish the two causes at a glance: one short match means the flag; one enormous match means the greed.
What is the difference between a match and a group?
The match is everything the whole pattern caught; groups are the parts captured by parentheses inside it, numbered by their opening bracket from the left, and named where the pattern uses (?<name>…). Groups are how extraction works: the match proves the pattern found the right place, and the groups decide what you actually take away. In the email example the matches are the addresses; groups 1 and 2 are the user and the domain, and the replacement preview is built entirely from the groups.
Why does ^ not match at the start of each line?
Because without the m flag, ^ means the start of the whole text, and $ means its end (or before a final newline). The m flag redefines both per line. This is the flag whose absence produces the most confusing regex behaviour in practice, a pattern that works on a one-line test and fails silently on real multi-line input, and the worked example above is built to make the difference visible in ten seconds.
How do I match a literal dot or dollar sign?
Escape it with a backslash: \. matches a dot rather than any character, and \$ matches a dollar sign. In the replacement string the roles flip: $ introduces group references, so a literal dollar there is written $$, and $& inserts the whole match. Half of replacement bugs are escaping mistakes on one side or the other of that divide, and the preview shows their effects immediately.
Related calculators
Method: the JavaScript RegExp engine executed in the visitor’s browser, matches enumerated with explicit zero-width advancement, results capped and labelled, exactly as defined on this page. Last reviewed: 13 October 2026.
