Regex Tester – Test, Highlight & Explain
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.