IT
OmnvertImage • Document • Network

Regex Tester – Test, Highlight & Explain

Validate and explain regex without risking slow matches.
Explain regex
\w: Word char
\b: Word boundary
.: Any char
+: One or more
\.: Literal dot
[]: Character class
{}: Quantifier
Matches
No matches yet.
Test text
Emails: jane@example.com john.doe@acme.co.uk URLs: https://omnvert.com http://localhost:3000/test
Other developer tools

About

A regular expression is a small parser squeezed onto one line, which is why it earns a scratchpad. Paste a real sample — the messy log line, the actual CSV column, the address that broke last week — choose your flags, and watch which spans light up. Most broken patterns are not exotic: an anchor in the wrong place, a quantifier that swallows more than you meant, or a character class that quietly excludes the one input that matters.

Character classes are the vocabulary. \d is one ASCII digit, [a-z] one lowercase letter, and \w expands to [A-Za-z0-9_] — the underscore is in, the hyphen is not, which is why \w+ stops short on a kebab-case slug. A leading caret negates: [^,] is any character except a comma. Quantifiers add repetition: ? for zero or one, * for zero or more, + for one or more, {3} for exactly three, {2,} for two or more. Inside a class most metacharacters relax: [.] matches a literal dot, no escape needed.

Quantifiers are greedy by default and that trips up nearly everyone. Run <.+> against <b>bold</b> and you match the whole string, because .+ takes everything it can and then hands back just enough characters for the closing > to match. Add a question mark to make it lazy: <.+?> stops at the first bracket. Lazy is not automatically cheaper; it backtracks from the other direction and on some inputs does more work.

Anchors match positions rather than characters. ^ and $ pin to the start and end of the input, and \b marks a word boundary, so \bcat\b skips over concatenate. Parentheses do two jobs: they group for quantification and they capture. When you only need grouping, write (?:...) so group numbers stay stable when someone inserts another pair above yours. Named groups also read better — (?<year>\d{4})-(?<month>\d{2}) gives you match.groups.year instead of match[1].

Lookahead and lookbehind assert without consuming. (?=...) demands what follows, (?!...) forbids it, and (?<=...) with (?<!...) do the same thing backwards. A password rule falls out naturally as a stack of lookaheads anchored at the start: ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{10,}$ — every assertion is checked at position zero, then .{10,} does the actual matching. Lookbehind is the one to check before shipping: V8 and Firefox have had it for years, Safari only since 16.4, so an older iOS device throws a SyntaxError at parse time on a pattern that runs fine on your laptop.

Flags change the rules for the entire match. g returns every match instead of the first, i ignores case, m lets ^ and $ hit at line breaks, and s allows the dot to cross a newline. The g flag has a sharp edge in JavaScript: a regex object carrying /g keeps a mutable lastIndex, so calling .test() twice on the same object alternates true and false. Build the regex inside the loop, reset lastIndex by hand, or drop g when you only want a boolean.

Nested quantifiers are where a pattern goes from slow to freezing the tab. ^(a+)+$ against twenty-five a characters followed by one X has to try an exponential number of ways to split the input before it admits failure. Production patterns hide the same shape: (\s*\w+)*, ([^,]*,)*, or an alternation whose branches can both match the same text. Flatten the repetition, make the inner class exclusive so the branches cannot overlap, and throw a hostile string at the pattern before it reaches a request handler. Anything built from user input also deserves a length cap and a timeout.

JavaScript's engine is not PCRE, and half the patterns copied from a Python or PHP answer need edits. There are no atomic groups, no possessive quantifiers, no recursion, and \A / \z do not exist — use ^ and $ without the m flag. Unicode has its own switch: \u00e9 is a plain BMP escape, but \u{1F600} and property escapes like \p{Letter} only parse when the u flag is on, and with u an emoji counts as one unit, not two surrogate halves.

Some jobs are the wrong shape for a regex. HTML and JSON nest arbitrarily deep, and no regular expression can count balanced pairs, so parse with a DOM parser or JSON.parse and query the result instead. Email validation is the other classic trap: a 6,000-character pattern still accepts addresses that bounce. Check for a single @ with something on either side, then send a confirmation link. Regex earns its keep on extraction and shape checks over flat text; the moment real structure matters, use a real parser.

FAQ

Which flags can I use?
Global, ignore case, multiline, dotAll, unicode and sticky flags are supported.
How do you prevent ReDoS?
Execution happens in a Web Worker with length limits and a 150 ms timeout.
Can I see capture groups?
Yes. Matches list includes index and captured groups per result.
How does replace work?
Provide a replacement string; we show the transformed text and let you copy it.
What does explain regex do?
We map common tokens (\d, \w, +, ?, ^, $, [], (), |, etc.) to short English/Turkish hints.
Why does test() return true, then false, on the same string?
A regex literal with the g or y flag keeps a mutable lastIndex property, and .test() advances it after every successful match. The second call starts searching past the end of the string and fails. Either drop the g flag when you only need a boolean, reset re.lastIndex = 0 before each call, or construct the regex fresh inside the loop.
What is the practical difference between .* and .*?
.* is greedy: it consumes to the end of the line and backtracks only as far as it must for the rest of the pattern to succeed. .*? is lazy: it starts with zero characters and expands one at a time. Against a line with two quoted strings, "(.*)" captures everything between the first and last quote while "(.*?)" captures just the first quoted value.
Why doesn't \d match Arabic-Indic or Devanagari digits?
In JavaScript \d is defined as exactly [0-9] and never widens, which is usually what you want for parsing IDs. If you genuinely need any Unicode decimal digit, turn on the u flag and use \p{Nd}. Be deliberate about it — accepting non-ASCII digits in a field you later pass to parseInt tends to end badly.
When do I want the m flag versus the s flag?
They solve unrelated problems. m (multiline) makes ^ and $ match at every line break instead of only at the string boundaries, which is what you want when scanning a log dump line by line. s (dotAll) makes . match newline characters too, which is what you want when a single logical record spans several lines.
My pattern hangs the browser. What is going on?
Almost certainly catastrophic backtracking from nested or overlapping quantifiers, such as (a+)+ or (\w+\s?)+. On a non-matching input the engine explores exponentially many ways to divide the text. This tester runs patterns in a Web Worker with a timeout so a bad pattern cannot lock the page, but the same pattern on a server would pin a CPU core.
Can a regex match nested brackets or HTML tags?
Not in JavaScript. Balanced nesting requires counting, which a regular language cannot do; PCRE and .NET fake it with recursion and balancing groups, and JavaScript has neither. Match a flat, well-defined token with a regex and hand anything nested to DOMParser, an HTML parser, or JSON.parse.
Why bother with non-capturing groups?
(?:...) groups for quantification or alternation without allocating a capture slot. That keeps $1, $2 and match[1] pointing at the same things after someone adds a group in the middle of the pattern, and it saves the engine from recording spans you never read. Use a capturing group only when you actually want the text back.