Java Regex Tester — java.util.regex.Pattern, Translated Live
Write patterns in java.util.regex dialect — POSIX-style \p{Alpha} properties, \Q...\E literal quoting, named groups as (?<host>...) — and the translator expands each to its JavaScript equivalent in real time, showing every rewrite. Constructs Java supports that JavaScript cannot express, like possessive quantifiers, are rejected with the exact reason.
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.
Java regular expressions, in depth
Engine: java.util.regex.Pattern on the JVM. The tester translates your pattern for live evaluation — these sections document how Java's engine really behaves and where it differs from the evaluation engine.
Pattern and Matcher — the API this tester mirrors
Java regex lives behind two classes: Pattern.compile() turns source text into a compiled pattern, and Matcher walks a subject string. The API's three entry points map directly onto what this tester shows you: matcher.find() is the tester's global mode (each call advances to the next match, like the match table's rows), matcher.matches() requires the entire region to match (that is ^pattern$ here), and matcher.lookingAt() anchors at the start without requiring the end (that is ^pattern alone). If a pattern validates in global mode but your production matches() call returns false, the difference is anchoring — the highlight view shows exactly where the match stopped short of the string end.
Group semantics carry over one-to-one. Java's numbered groups are m.group(1) through m.group(N); named groups — supported since Java 7 — are m.group("name"), and the tester's group panel shows the same names with the same values. The region API (matcher.region(start, end)) has no flag equivalent, but slicing the test string before pasting reproduces it exactly. One Java-only nicety worth knowing when you go back to code: Pattern is immutable and thread-safe, and compile once, reuse across threads is the idiomatic — and safe — pattern.
\p{...} — Java's POSIX shortcuts versus Unicode categories
Java accepts two \p{...} vocabularies that are easy to conflate. The POSIX-inspired shortcuts — \p{Alpha}, \p{Digit}, \p{Alnum}, \p{Upper}, \p{Lower}, \p{Punct}, \p{XDigit} — are ASCII-only by default, matching exactly [A-Za-z] and friends; with Pattern.UNICODE_CHARACTER_CLASS they widen to Unicode. The general-category escapes — \p{L} (letter), \p{N} (number), \p{Lu} (uppercase letter) — are Unicode from the start. The tester expands the ASCII shortcuts to explicit classes (and notes that the UNICODE_CHARACTER_CLASS variant is broader), while general categories pass through with JavaScript's u flag added, since the two engines share that vocabulary.
Java's Is-prefixed block escapes — \p{IsBasicLatin}, \p{IsGreek} — have no reliable JavaScript equivalent: JavaScript exposes scripts (\p{Script=Greek}) and a different block taxonomy, so the translator rejects them with that explanation rather than mapping IsLatin to something approximately similar. If you need script-level matching in portable code, \p{Script=...} works in both Java (as \p{script=Greek}, Java 7+) and JavaScript with u — that is the shared, safe vocabulary.
\Q...\E and the escaping discipline
Java programmers meet \Q...\E through Pattern.quote(): it quotes literal text so metacharacters lose their meaning — invaluable when a user-supplied string must be matched verbatim inside a larger pattern. The translator expands \Q...\E to an explicitly escaped literal, so the quoted region survives translation with its meaning intact, and the notice tells you the expansion happened. In Java code, reach for Pattern.quote() at the boundary where data becomes pattern; here, paste the quoted form and the expansion is done for you.
The escaping discipline around user input is the same in both languages: never interpolate raw input into a pattern, because parentheses, brackets, and backslashes all change meaning. Java's Matcher also offers Matcher.quoteReplacement() for the replacement side — the fix for $ and \ in replacement strings — which maps to JavaScript's replacement-string escaping of $ as $$. Both are small APIs that prevent whole classes of bugs, and both are worth using even when the pattern side feels safe.
Anchors, boundaries, and the possessive wall
Java and JavaScript agree on more anchor semantics than most flavor pairs: \b word boundaries, \B non-boundaries, and lookbehind (Java's must be bounded-length; JavaScript's allows unbounded) all translate by passthrough. The difference concentrates at the string's end: Java's \z is the true absolute end, \Z matches before a final newline, and $ matches before a final newline unless MULTILINE makes it per-line. JavaScript's $ (no m) is Java's \Z exactly, so the translator maps \z and \Z both to $ and attaches the caveat — if you are validating that no trailing newline survives (trimmed fields, tokens), test with the newline present.
The hard wall is possessive quantifiers and atomic groups: a*+, a++, (?>ab) suppress backtracking into the atom, and JavaScript's engine simply cannot express that (the experimental v flag aside). The translator rejects them by name because dropping the + would change match results on crafted input — the exact failure a tester exists to prevent. The standard Java-side alternatives — rewriting with lookahead emulation or bounded alternation — work in both languages, and the ReDoS guard here still catches the catastrophic shapes possessive quantifiers were usually invented to fix.
Java versus the evaluation engine
What translates automatically, what carries a caveat, and what is rejected — construct by construct.
| Construct | Java | This tester |
|---|---|---|
| POSIX \p{...} shortcuts | \p{Alpha} etc., ASCII by default; Unicode with UNICODE_CHARACTER_CLASS | Expanded to exact ASCII classes; Unicode variant noted |
| \Q...\E | Literal quoting (also Pattern.quote()) | Expanded to an explicitly escaped literal |
| String end | \z absolute; \Z before final newline; $ like \Z unless MULTILINE | $ maps to \Z behavior — caveat attached |
| Possessive / atomic | a*+, a++, (?>...) supported since Java 1.4 | No equivalent — rejected with the reason |
| Lookbehind | Must be finite, bounded length | Allows unbounded — patterns legal here may fail to compile in Java |
| Named groups | (?<name>...) since Java 7 | Identical syntax — passes through |
| Matching model | find() iterates; matches() requires full match | Global mode is find(); ^…$ emulates matches() |
| Blocks | \p{IsBasicLatin} and Is-block escapes | Not supported — use \p{Script=...}, shared by both |
Java regex cheat sheet
The Java-specific tokens, with the constructs that cannot be translated marked explicitly.
| Token | Meaning | Example |
|---|---|---|
| \p{Alpha} | ASCII letter (Java POSIX shortcut) | \p{Alpha}+ → Acme |
| \p{Digit} | ASCII digit | \p{Digit}{3} → 123 |
| \p{Alnum} | ASCII letter or digit | \p{Alnum}+ → a1B2 |
| \p{L} | Unicode letter (general category) | \p{L}+ → naïve |
| \Q…\E | Quote literal text | \QC++\E → literal C++ |
| (?<name>…) | Named group (Java 7+) | (?<host>\w+\.\w+) |
| \A \z \Z | Start / absolute end / end before final newline | \A\d+\z |
| \b | Word boundary | \bvoid\b → not avoid |
| a*+ | Possessive quantifier — not translatable | blocked with a reason |
| (?>ab) | Atomic group — not translatable | blocked with a reason |
Frequently asked questions
Why is my possessive quantifier rejected?
Java's a*+ and (?>...) disable backtracking into the atom — a performance control JavaScript's engine cannot express. Dropping the + or unwrapping the atomic group would leave a pattern that matches differently on crafted input, so the translator refuses with a message saying exactly that. If the possessive quantifier was there to fight catastrophic backtracking, the tester's two-second ReDoS budget will surface whether the non-possessive form is actually safe.
Does \p{Alpha} match Unicode letters here?
Not by default — and that is correct Java behavior. \p{Alpha} is ASCII-only unless the pattern is compiled with Pattern.UNICODE_CHARACTER_CLASS, in which case it widens to Unicode letters. The tester expands \p{Alpha} to its exact ASCII class and attaches a notice about the wider variant. For intentionally Unicode matching, use the general-category escape \p{L} instead: it is Unicode-first in Java and translates here with the u flag added automatically.
How do \z and \Z translate, and why do I care?
Java's \z matches only the true end of input; \Z also matches just before a single trailing newline — and JavaScript's $ (without the m flag) behaves exactly like \Z. The translator maps both to $ and documents the difference in a notice. It matters when validating fields where a trailing newline must not pass: with \z semantics in mind, keep a newline at the end of your test string and check whether the match still extends to it.
Can I test lookbehind that Java would reject?
Possibly, yes — and that is a real fidelity boundary. Java requires lookbehind alternatives to have bounded, computable length; JavaScript accepts unbounded lookbehind like (?<=ab*)c. A pattern using unbounded lookbehind can therefore validate here and still throw when compiled in Java. Keep Java's restriction in mind for lookbehind containing * or +; fixed-length lookbehind like (?<=\$) works identically in both.
How does this tester's output map to Matcher methods?
The match table is matcher.find() in a loop: one row per match, in order, with span offsets like m.start() and m.end(). The named-group panel is m.group("name"); the whole-match row is m.group(). Turning global mode off gives you a single matcher.find() call. matcher.matches() — the full-string variant — is expressed by anchoring with ^ and $, and matcher.lookingAt() by a leading ^ alone.
Is Pattern.quote() the same as \Q...\E?
Pattern.quote("text") returns \Qtext\E — the API method is literally a wrapper around the quoting syntax, with a fallback to escaping when the text contains \E. The tester expands any pasted \Q...\E region to an explicitly escaped literal, so both forms work. The rule it enforces is the one worth internalizing: data interpolated into patterns must be quoted, whether by Pattern.quote(), \Q...\E, or — in your own Java code — by not building patterns from user input at all.