Python Regex Tester — re Module Patterns, Translated Live
Write the pattern the way it appears in re.compile() — named groups as (?P<year>...), backreferences as (?P=year), anchors as \A and \Z — and the translator rewrites each construct to its JavaScript equivalent in real time, telling you exactly what changed and stopping with a precise message when a construct (verbose mode, atomic groups) has no faithful translation.
Zero upload, ever
Patterns and test data are evaluated in your browser — connection strings and column values never touch a server.
Live translation
The dialect translator and the match engine recompute on every keystroke, with notices explaining each rewrite.
Honest where it matters
Constructs that cannot be translated faithfully are rejected with a precise reason — never silently mis-matched.
Test string
Matches highlight as you type
Match results
Sandboxed Web WorkerType a pattern to see live matches.
Python regular expressions, in depth
Engine: Python re module, CPython's sre engine. The tester translates your pattern for live evaluation — these sections document how Python's engine really behaves and where it differs from the evaluation engine.
The re module's dialect, translated construct by construct
Python's re module grew its named-group syntax years before JavaScript standardized one, so the two languages spell the same idea differently: Python writes (?P<name>...) and references the group later as (?P=name), while JavaScript (and PCRE) write (?<name>...) and \k<name>. The translator rewrites both forms on the fly and shows the rewritten pattern, so what you see in the match table uses your names — the group panel reports year, month, user, or whatever you named them, exactly as m.group('year') would in Python. Numbered backreferences \1 through \99 pass through untouched; both engines agree on them.
The anchors are where semantics, not just spelling, differ. Python's \A and \Z are absolute — they mean the true start and end of the string, unaffected by any flag. JavaScript's ^ and $ match at line boundaries when the m flag is on, and JavaScript's $ has one extra quirk: without m it also matches just before a single trailing newline, which Python's \Z never does. The translator maps \A to ^ and \Z to $ and attaches a notice reminding you to keep m off — and if you are validating input where a trailing newline must not match (password rules, API tokens), test with the newline actually present.
Unicode by default — the biggest invisible difference
In Python 3, patterns compiled from str are Unicode by default: \w matches word characters from every script, \d matches non-ASCII digits, and \s includes Unicode whitespace. JavaScript is the opposite — without the u flag its shorthand classes are ASCII-only, and \d rejects the Arabic-Indic digits Python happily matches. The tester flags every \d, \w, and \s with a notice recommending the u flag for full parity, because on internationalized data the difference is real rows matched versus missed. Python's (?a) ASCII-only mode matches JavaScript's default behavior, so patterns written with (?a) translate with no extra care needed.
The dot deserves its own sentence: Python's . matches any character except newline, and re.DOTALL lifts that — the same shape as JavaScript's s (dotAll) flag, which the flag bar exposes directly. Python's re.MULTILINE maps to JavaScript's m flag one-to-one; both re-anchor ^ and $ at line boundaries. The one flag with no translation is re.VERBOSE — see the FAQ — because ignoring whitespace and # comments cannot be faked safely by a translator and would silently change what matches.
From tester to code: the API mapping
The tester's global mode is re.findall() and re.finditer(); its single-match mode is re.search(). The mapping continues usefully into code you write afterward: match.group('name') in Python becomes match.groups.name here and match.groups.name in JavaScript's result objects; groupdict() becomes the named-group panel; re.sub(r'(?P<h>\d+)', r'\g<h>', s) becomes s.replace(regex, '$<h>') with the same named-reference idea in the replacement string. If you validate a pattern here with named groups, the names carry all the way into your production code — that is deliberate.
Two API-level behaviors worth rehearsing before you trust a pattern. re.match() anchors at the start of the string (an implicit \A) while re.search() scans — JavaScript's .test() and exec() are search-shaped, so add ^ yourself when you mean match(). And re.fullmatch() requires the pattern to consume the entire string, which you express here as ^pattern$ (with the trailing-newline caveat above) — the tester's highlight view makes partial matches obvious in a way fullmatch's silent False never does in code.
Backtracking, ReDoS, and the two-second ceiling
Python's sre engine and JavaScript's engine are both backtracking engines, which means they share the same failure mode: patterns with nested quantifiers — (a+)+$, (a|a)*, (a*)*b — take exponential time on non-matching input. In a web service that is a denial-of-service vector with a CVE-style name (ReDoS); in CPython it is a hung request. This tester runs every evaluation in a sandboxed Web Worker with a two-second budget and reports Catastrophic Backtracking Detected with the offending shape called out, so the pattern fails here — loudly — instead of in production, quietly.
The structural fixes are the same in both languages. Bound the repetition, prefer character classes over alternation ((a|b)+ becomes [ab]+), anchor patterns so failure happens early, and for untrusted input consider whether a regular expression is even the right tool. If your service must run untrusted patterns, the industrial answer in Python is the google-re2 library, which guarantees linear time by giving up backreferences and lookarounds — the same trade-off this tester's timeout makes visible.
Python versus the evaluation engine
What translates automatically, what carries a caveat, and what is rejected — construct by construct.
| Construct | Python | This tester |
|---|---|---|
| Named groups | (?P<name>...) and (?P=name) | (?<name>...) and \k<name> — rewritten automatically |
| Shorthand classes | Unicode by default (str patterns) | ASCII by default; u flag needed for parity — flagged in notices |
| String-end anchor | \Z never matches before a trailing newline | $ (no m) does match before one trailing newline — notice attached |
| Inline flags | (?i) allowed anywhere, can be scoped with (?i:...) | Flags are whole-pattern only; leading (?i) is promoted, scoped forms are blocked |
| Verbose mode | re.VERBOSE / (?x) ignores whitespace and # comments | No equivalent — rejected with guidance |
| Atomic groups & possessive | Python 3.11+ supports (?>...) and *+ | No equivalent — rejected with guidance |
| match() vs search() | re.match anchors at start; re.search scans | test/exec always scan; add ^ to emulate match() |
| Replacement refs | \g<name> in re.sub | $<name> in replace() |
Python regex cheat sheet
The Python-specific tokens, with the constructs that cannot be translated marked explicitly.
| Token | Meaning | Example |
|---|---|---|
| (?P<name>…) | Named capture group (Python spelling) | (?P<year>\d{4}) → groups.year |
| (?P=name) | Backreference to a named group | (?P<w>\w+) (?P=w) → repeated word |
| \A … \Z | Absolute string start / end | \A\d+\Z → digits only |
| \d \w \s | Unicode-aware digit / word / space (Python 3 default) | \w+ → naïve OK |
| (?i) (?m) (?s) | Inline flags: ignore case, multiline, dot-all | (?i)error → ERROR too |
| (?a) | ASCII-only shorthand classes | (?a)\w+ → ASCII words |
| r'…' | Raw string — keep backslashes for the engine | r'\d+' not '\d+' |
| (?(id)yes|no) | Conditional pattern — not translatable to JS | blocked with a reason |
| (?>…) | Atomic group (Python 3.11+) — not translatable to JS | blocked with a reason |
| re.VERBOSE (?x) | Free-spacing mode — not translatable to JS | blocked with a reason |
Frequently asked questions
Why does (?P<name>...) work here — isn't that Python-only syntax?
The translator rewrites it to JavaScript's (?<name>...) spelling before evaluation and shows the rewrite in the notices, so your pattern stays in Python syntax while the engine runs the translated form. The names you choose flow through to the match table's group panel the same way m.group('name') returns them in Python. (?P=name) backreferences get the same treatment and become \k<name>.
How do I test a re.VERBOSE pattern with comments and whitespace?
You cannot translate it automatically — ignoring whitespace and # comments is exactly the kind of semantic change a translator must not guess at, so verbose patterns are rejected with an explanation. Strip the comments and whitespace manually (or preprocess the pattern string with Python's re.VERBOSE rules in your own code) and test the condensed form here. The condensed pattern is what the engine ultimately matches anyway.
Does $ in this tester behave like Python's $?
Almost. Python's $ matches at the end of the string and just before a newline at the end, and per line with re.MULTILINE — exactly like JavaScript's $ with and without the m flag. The difference hides in \Z: Python's \Z is the true absolute end and never matches before a trailing newline, while the JavaScript $ used to translate it does. The notice attached to every \Z translation calls this out; when the distinction matters, keep a trailing newline in your test string and check whether the match extends past it.
Are Python 3.11 atomic groups and possessive quantifiers supported?
No — they are rejected with a specific message. Atomic groups (?>...) and possessive quantifiers like a*+ or a++ prevent the engine from backtracking into the match, a performance guarantee JavaScript's engine cannot express (outside the experimental v flag). Silently dropping the possessive + would change results on crafted input, so the translator refuses rather than lies. ReDoS-shaped patterns still get caught by the two-second execution budget.
How do I match Unicode word characters the way Python does?
Enable the u flag. Python 3 matches \w, \d, and \s against Unicode by default — naïve, संस्कृत, and ABC are word text — while JavaScript's defaults are ASCII-only. The tester attaches a notice to every shorthand class reminding you of this; with u on, the classes widen to code-point semantics close to Python's. For the reverse — forcing ASCII in Python — (?a) matches JavaScript's default behavior, so those patterns translate cleanly.
Which Python constructs are rejected outright?
Verbose mode (?x), scoped or negative inline flags like (?i:...) and (?-i), atomic groups (?P>...) and (?>...), conditionals (?(group)yes|no), and the \G anchor. Each is rejected with a message naming the construct and the reason — because in every case a faithful translation does not exist, and a silent approximation would report matches your Python code would not produce. Everything else — named groups, backreferences, lookaround, lazy quantifiers — translates or passes through natively.