Regex Tester
Enter a regular expression and test text to see matches, capture groups and highlighted positions in real time. Everything runs locally in your browser.
A regex is a mini-language for describing text shapes, not a single feature
Many people treat a regular expression as "one button in a search box", but the more useful mental model is: it is a small language of special characters that describes what a piece of text looks like. For example, \d{4}-\d{2}-\d{2} describes the shape "four digits, hyphen, two digits, hyphen, two digits" — not a specific date — and ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ describes the structure "local-part@domain". Because it is a shape description rather than a fixed value, the same pattern is reusable across email, logs, IDs and URLs. To use it well you only need to understand three things: how characters are written (classes, escaping), how repetition is written (quantifiers, greedy vs lazy) and how structure is grouped and anchored (groups, assertions, ^ $). This tool builds and runs the pattern locally in your browser's ECMAScript engine; the tables below help you avoid the most common traps.
Regex engine families: why the same pattern behaves differently elsewhere
The same regex can "run / throw / give different results" in a browser, PHP, Java or Python because the underlying engine differs. Mainstream engines fall into two camps: backtracking NFA (the default for most languages) and DFA / automaton (RE2 and friends: linear-time, never backtracks). The table contrasts the runtimes developers meet most often, based on the ECMA-262 15th edition (ES2024), PCRE2, Python re and Java java.util.regex documentation.
| Engine / implementation | Typical use | Backtracking behaviour | Key syntax differences |
|---|---|---|---|
| ECMAScript (browser / Node.js, what this tool uses) | Front-end validation, JS scripts | Backtracking NFA; greedy by default, lazy *? supported, possessive quantifiers unsupported | \p{...} needs the u flag; named groups (?<name>...); under u, \d matches all Unicode digits |
| PCRE2 (PHP, Notepad++, Apache, etc.) | Server side, editors | Backtracking; possessive *+ and atomic groups (>...) mitigate ReDoS | Richest syntax; \p{L}, backtracking control (*SKIP) |
RE2 (Go regexp, some Rust crates) | High-concurrency servers | DFA / automaton, linear time, never backtracks, inherently ReDoS-safe | No backreferences, no lookbehind; a syntax subset |
Python re (CPython default) | Data cleaning, scraping | Backtracking; no possessive quantifiers; atomic grouping as an experimental feature since 3.11 | \p needs the third-party regex library; . does not match newline by default |
Java java.util.regex | Enterprise back ends | Backtracking; possessive *+ and atomic groups (>...) supported | \p{L}, named groups (?<name>...); matches() anchors the whole string by default |
Note: this tool runs on the browser's ECMAScript engine, so a possessive quantifier like *+ throws a syntax error here. If your pattern comes from Java or PCRE and uses *+, rewrite it or drop the possessive modifier. The difference from server-side PCRE/Java is the most common source of integration surprises.
Backtracking vs DFA: how one bad pattern can take down a service (measured)
A backtracking engine "guesses": the + quantifier first eats as much as possible, and when the rest fails to fit it "spits back" and re-partitions. Usually that is fast, but certain nested quantifiers — such as (a+)+$ — make the number of re-partitions grow exponentially with input length. That is ReDoS (Regular Expression Denial of Service). In web contexts, an attacker only needs to send a crafted long string to peg the validation endpoint's CPU. OWASP (Top 10:2021, A06) lists ReDoS as a classic web risk. The table below is measured on this machine (Node v22 / 12th-gen Intel i5-12600KF; input is n copies of a followed by one !, for relative comparison only):
| Input length (n a's + one !) | (a+)+$ time (ms) | Linear a+ time (ms) | Note |
|---|---|---|---|
| 18 | 2.22 | 762 | Both fast at short length |
| 20 | 8.49 | 734 | Catastrophic backtracking begins |
| 22 | 31.90 | 769 | Roughly x4 per +2 length |
| 24 | 128.24 | 788 | Already 6x the linear version |
| 26 | 517.60 | 818 | Half a second; noticeable lag |
| 28 | 2316.20 | 786 | Over 2s; endpoint effectively down |
| 30 | 15446.63 | 824 | About 15s; service taken down |
The contrast is clear: the linear regex a+ stays roughly flat (~780ms, first-compile noise) while the catastrophic version climbs from 2ms at 18 letters to 15 seconds at 30 — about 7000x. Rule of thumb: avoid "quantifier inside quantifier" where both can match the same character (e.g. (a+)+, (a*)*, (.*a){10}). Validate user input with simple, deterministic patterns on both client and server, and prefer a linear engine like RE2 when you can. This tool highlights matches in real time but does not block ReDoS-prone patterns for you — stress-test complex regexes with longer input yourself.
Metacharacters & quantifiers cheat sheet
This table covers about 90% of daily usage. Key point: in ECMAScript there is only greedy and lazy, never possessive; to get the anti-backtracking effect of "possessive" you must restructure (e.g. use [^x]+ instead of .*).
| Symbol | Meaning | Greedy / lazy / possessive | Note |
|---|---|---|---|
* | Previous item, 0 or more times | greedy * / lazy *? / possessive *+ (ECMAScript unsupported) | Most common ReDoS source |
+ | Previous item, 1 or more times | greedy + / lazy +? / possessive ++ (ECMAScript unsupported) | At least one |
? | Previous item, 0 or 1 time | greedy ? / lazy ?? / possessive ?+ | Also makes a group non-capturing (?:...) |
{n,m} | Previous item, n to m times | greedy {n,m} / lazy {n,m}? / possessive {n,m}+ | {3,} at least 3, {3} exactly 3 |
\b | Word boundary (zero-width) | — | Matches a position, not a character |
\d | Digit | — | Without u, only [0-9]; with u, all Unicode digits |
\w | Word character | — | [A-Za-z0-9_]; not Chinese even under u |
\p{L} | Unicode letter property | — | Needs u flag; \p{Script=Han} matches Han characters |
Flags reference
Flags decide how to search, not what. This tool exposes g / i / m / s / u; the ECMAScript y (sticky) flag is listed for completeness:
| Flag | Effect | Tool default |
|---|---|---|
g | Global: find all matches, not just the first | On by default |
i | Ignore case: a also matches A | Off by default |
m | Multiline: ^ $ anchor each line's start/end | Off by default |
s | dotAll: let . also match newline | Off by default |
u | Unicode code-point mode: enables \p{...}, correct surrogate handling | Off by default (turn on for non-Latin text) |
y | Sticky: match only at lastIndex; common in front-end code | Not a UI toggle; write it inline if needed |
Common pattern library: patterns and known limits
These are high-frequency patterns pulled from real projects, all compile-verified on this machine. But regex validation is "shape validation", not "semantic validation" — each row also notes what it fails to catch, which is exactly where such tools are most often misused.
| Purpose | Pattern | Known limits |
|---|---|---|
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ | No quoted local parts, no IDN domains, no consecutive dots; passes weak addresses like a@b.c | |
| URL (http/https) | ^https?:\/\/[\w.-]+(?:\/[\w./?%&=#-]*)?$ | No port check, no ftp, no TLD validation; shape only |
| Date YYYY-MM-DD | ^\d{4}-\d{2}-\d{2}$ | Passes invalid calendar values like 2026-13-45; needs a separate date sanity check |
| IPv4 address | ^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)$ | Allows leading zeros like 192.168.001.1; does not reject reserved blocks like 0.0.0.0 |
| International phone (E.164) | ^\+[1-9]\d{1,14}$ | Only checks the +CountryCode format; does not verify the number is allocated or in service |
| Password strength | ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$ | Only checks "lower + upper + digit + symbol + 8 chars"; passes trivial passwords like Password1!. Pair it with a password generator and the password security guide |
Three real examples (using the page default input)
All three examples use this tool's default test text 2026-08-23 released a new version, 2026-09-01 update planned. (only the g flag checked by default); paste them straight in to reproduce. To double-check how many characters or words a text holds, the word counter helps; to compare two revisions of a text, use the text diff tool.
Example 1: count all dates
Pattern \d{4}-\d{2}-\d{2}, flag g. On the default text this yields 2 matches: 2026-08-23 (position 0) and 2026-09-01 (position 35). This is exactly the placeholder example — the most direct way to "pull out every date".
Example 2: split year / month / day into capture groups
Pattern (\d{4})-(\d{2})-(\d{2}), flag g. Still 2 matches, but each now has 3 capture groups: group 1 year (2026), group 2 month (08 / 09), group 3 day (23 / 01). The tool's "Capture Groups" list shows them so you can extract by field.
Example 3: extract every number
Pattern \d+, flag g. The default text has 6 numeric matches: 2026@0, 08@5, 23@8, 2026@35, 09@40, 01@43. Notice \d+ treats "2026" as one number, not four single digits — that is the quantifier "eating greedily".
Common regex pitfalls
① Escaping. . * + ? ( ) are special; to match them literally add a backslash (\. \*). In a programming-language string the backslash itself often needs escaping too — the #1 reason "it looks right but throws".
② Greedy vs lazy. Quantifiers are greedy (match as much as possible) by default; add ? for lazy (.*?). Getting this wrong when extracting HTML tags can swallow half an article in one match.
③ Forgetting anchors. To validate "the whole string is an email", wrap it in ^ and $; otherwise it matches only a substring and the validation "leaks".
Frequently asked questions
Why does the same regex run here but error or differ in Python / Java? Different engine dialects (see the engine-family table above). The sharpest gaps: ECMAScript has no possessive quantifier *+ while Java/PCRE do; PCRE's (*SKIP) backtracking control has no JS equivalent; Python re does not match newline with . by default, unlike JS with s. Check the table for syntax portability before moving a pattern across languages.
Why does \d match a different range with and without the u flag? Without u, \d is only [0-9] (ASCII digits). With the u flag, ECMAScript treats it as Unicode, so \d also matches Arabic, full-width and other scripts' digits. If your text may contain full-width digits like "2026", turn on u or the validation misses them.
How do I validate an email without a regex that hangs? Email/URL checks are usually safe on their own; the risk is "nested quantifier + long input". Rule: use the flat patterns in the library above, never something like ([\w]+@[\w]+)+; truncate over-long user input before validating, or use a linear engine like RE2. More on breach and weak-password defence in the password security guide.
Do many capture groups hurt performance? Is a named group (?<name>...) different from an anonymous one? They are functionally equal; named groups just make output readable and stop later code breaking when a group is inserted and shifts the indices. The performance cost is tiny; what actually bites is "nested capture groups + catastrophic backtracking", independent of whether groups are named.
Why does my browser freeze on a complex regex over a large text? Almost certainly ReDoS (see the measured table). The browser is also a backtracking engine, and (a+)+ over long input explodes exponentially. To diagnose: stress-test with longer input and watch whether time climbs steeply with length; if so, replace nested quantifiers with character-class boundaries (e.g. [^x]+) or split into several simple passes.