Skip to content
100% Client-SideC# / .NET (System.Text.RegularExpressions)Dialect-Aware Translation

.NET Regex Tester — C# System.Text.RegularExpressions, Live

Write patterns the way they appear in new Regex(...) or Regex.Match — named groups in either .NET spelling, leading inline flags, RegexOptions-shaped semantics — and the translator normalizes them for the evaluation engine, showing each rewrite. Constructs that cannot travel, like balancing groups and scoped (?i:...), are rejected with precise reasons.

  • 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 Worker

Type a pattern to see live matches.

.NET regular expressions, in depth

Engine: System.Text.RegularExpressions.Regex (.NET 6/7/8). The tester translates your pattern for live evaluation — these sections document how .NET's engine really behaves and where it differs from the evaluation engine.

Regex, Regex.Match, and the shape of everyday .NET code

.NET regex usage clusters around a few idioms: the static Regex.Match(subject, pattern) and Regex.Matches(...) for one-off calls, instance regexes for reuse, and — since .NET 7 — the generated source generator ([GeneratedRegex(...)]) that compiles the pattern at build time. The tester's global mode is Regex.Matches(): one row per Match in the returned MatchCollection, with named groups per row exactly like match.Groups["name"].Value. Single-match mode is Regex.Match(); Success is the tester's 'no matches found' state.

The APIs the tester cannot simulate are still worth planning around. .NET's Regex supports a match timeout (TimeSpan) precisely because catastrophic backtracking takes down services — the same threat model this tester enforces with its two-second worker budget and Catastrophic Backtracking Detected report. RegexOptions.IgnoreCase, Multiline, and Singleline map to the i, m, and s flags on the flag bar; IgnorePatternWhitespace is .NET's verbose mode and, like PCRE's x, is rejected rather than approximated. If your production pattern carries a timeout, treat a tester rejection as your timeout firing early.

Named groups in both spellings — and the balancing-group wall

.NET accepts two named-group spellings — (?<name>...) and (?'name'...) — and the translator normalizes both to the form JavaScript shares. Named groups are the backbone of the nicest .NET regex feature set: match.Groups["name"], ${name} substitution in Regex.Replace, and named capture in routed templates. The tester's group panel speaks the same vocabulary, so a pattern validated here carries its names straight into production code. One spelling caution: a hyphenated name like (?<a-b>...) is not a name at all — it is a balancing group definition, and it is rejected with that exact explanation.

Balancing groups — (?<'Before-After'>...) — are .NET's mechanism for matching nested balanced constructs (every open paren has its close), built on the stack discipline of previous captures. They are genuinely useful and genuinely untranslatable: no other mainstream engine has them. The honest rejection message points at the alternatives that do travel — parser combinators, recursive descent, or counter-based matching in code — which is the same advice the .NET team gives for expression-sized problems that outgrow regex.

Inline flags, RegexOptions, and Unicode defaults

.NET allows inline flag letters anywhere in a pattern and can scope them with (?i:...) groups — a subexpression that is case-insensitive while the rest of the pattern stays sensitive. JavaScript supports flags only whole-pattern, so the translator promotes leading (?i), (?m), and (?s) to flags and rejects scoped or mid-pattern forms with an explanation, because dropping the scope would change what matches. The negative forms (?-i) and composite (?im-sx) are rejected for the same reason: turning flags off midway is not a thing JavaScript can do.

The Unicode defaults run opposite to Java's: .NET's \d, \w, and \s are Unicode-aware unless RegexOptions.ECMAScript narrows them, while JavaScript's are ASCII unless u widens them. The translator attaches a notice to every shorthand class recommending the u flag for parity, and that notice is not decoration — on internationalized data, ASCII-only matching silently skips rows. General categories and scripts (\p{L}, \p{IsGreek} on the .NET side versus \p{Script=Greek} in JavaScript) pass through where the vocabularies overlap, and the Is-block escapes .NET uses are rejected where they do not.

Anchors, substitutions, and the $<newline> corner

The anchor map is the one to keep close. .NET's \A is absolute start (JavaScript's ^ with m off); .NET's \z is absolute end; .NET's \Z matches before a final newline — which is exactly JavaScript's $ with m off. The translator maps \A, \z, and \Z onto ^ and $ with a notice documenting which corner case differs: JavaScript's $ matching before one trailing newline. In .NET, $ without RegexOptions.Multiline matches at the end of the string or before a final newline — the same shape — so multiline workflows map cleanly: .NET MultILINE is JavaScript m, anchoring ^ and $ per line in both.

Replacement strings round out the portability story: .NET's ${name} and $1 substitutions in Regex.Replace map to JavaScript's $<name> and $1 in String.replace(), with the same capture numbering the group panel shows. .NET's substitution extras — $$ for a literal dollar, $& for the whole match — have JavaScript twins with the same spelling. The one substitution API without a twin is MatchEvaluator (the lambda form of Regex.Replace); its equivalent is JavaScript's replace with a function callback, which is the same idea in different clothes.

.NET versus the evaluation engine

What translates automatically, what carries a caveat, and what is rejected — construct by construct.

Syntax differences between .NET regex and JavaScript RegExp
Construct.NETThis tester
Named groups(?<name>...) and (?'name'...)(?<name>...) — both spellings normalized
Balancing groups(?<a-b>...) match nested balanced constructsNo equivalent — rejected with guidance
Inline flagsAnywhere, scoped with (?i:...), negative with (?-i)Whole-pattern only; leading forms promoted, scoped rejected
Shorthand classes\d \w \s are Unicode by defaultASCII by default; u flag needed for parity — flagged
String end\z absolute; \Z before final newline; $ like \Z$ (no m) behaves like \Z — caveat attached
TimeoutsRegex(...) accepts a TimeSpan match timeoutThis tester's 2-second worker budget is the equivalent guard
Substitutions${name}, $1, $&, $$ in Regex.Replace$<name>, $1, $&, $$ in String.replace()
Unicode blocks\p{IsBasicLatin} and Is-block escapesScripts and categories instead — \p{Script=Latin} with u

.NET regex cheat sheet

The .NET-specific tokens, with the constructs that cannot be translated marked explicitly.

.NET regex token cheat sheet
TokenMeaningExample
(?<name>…)Named group (.NET spelling #1)(?<area>\d{3})
(?'name'…)Named group (.NET spelling #2)(?'line'\d{4})
(?<a-b>…)Balancing group — not translatableblocked with a reason
(?i) (?m) (?s)Inline flags at pattern start(?i)error → ERROR
(?i:…)Scoped inline flag — not translatableblocked with a reason
\A \z \ZStart / absolute end / end before final newline\A\d+\z
\d \w \sUnicode by default in .NET\w+ → naïve OK
${name}Named substitution in Regex.Replace${area}-${line}
$&Whole match in substitution"$&!"
TimeSpanMatch timeout — echoed by the 2s ReDoS guard herenew Regex(p, opts, TimeSpan.FromSeconds(1))

Frequently asked questions

Why is my balancing group rejected?

Balancing groups — (?'Open-Close'...) or (?<Open-Close>...) — maintain a capture stack and are .NET's mechanism for matching nested balanced text like parentheses or XML tags. No other mainstream engine has them, so there is no translation that preserves meaning; the rejection names the construct. For nested structures, use a parser (or counter-based scanning in code) — the same direction the .NET documentation itself points when regex outgrows the problem.

Does (?i) work the same here as in C#?

At the start of a pattern, yes — the translator promotes (?i) to the i flag and notes it. The .NET-only superpowers are rejected: scoped groups like (?i:subexpression), mid-pattern flags, and negative forms like (?-i) cannot be expressed whole-pattern in JavaScript, and approximating them would change what matches. If you need scoped case-insensitivity, split the pattern so the insensitive part is its own pattern, or restructure with explicit [Aa] classes.

Why does \w here only match ASCII until I add u?

Because the engines' defaults are opposite: .NET's \d, \w, and \s are Unicode-aware unless RegexOptions.ECMAScript narrows them, while JavaScript's are ASCII-only unless the u flag widens them. The translator attaches a notice recommending u for parity, and with u on, the classes cover code-point text close to .NET's behavior. Test with the actual international characters you expect — that is the only way to see which side of the default your data lands on.

How do the 2-second budget and .NET's Regex timeout compare?

They exist for the same reason. .NET lets you pass a TimeSpan to new Regex(pattern, options, timeout) — or set Regex.MatchTimeout app-wide — so a catastrophic pattern throws RegexMatchTimeoutException instead of hanging a thread. This tester enforces the same discipline client-side: evaluation runs in a Web Worker with a two-second budget, and exceeding it reports Catastrophic Backtracking Detected with the offending shape. A pattern rejected here is a pattern that would burn your .NET timeout too — fix the shape, not the timeout.

Can I test RegexOptions.IgnorePatternWhitespace patterns?

No — verbose mode is rejected, because ignoring whitespace and # comments inside the pattern is a semantic transformation a translator cannot perform safely (which whitespace is ignored changes what matches). Write the pattern without internal whitespace and comments here. In C# code, keep using IgnorePatternWhitespace for readability — then test the de-commented, de-whitespaced form here, since that is what the engine ultimately compiles anyway.

How do I map this tester's groups to C# code?

The group panel is match.Groups["name"].Value for named groups and match.Groups[i] for numbered ones; the offsets column is match.Index and match.Index + match.Length. Global mode is Regex.Matches() iterating a MatchCollection; single-match mode is Regex.Match(). Substitutions translate directly: ${name} becomes $<name> and $& stays $& when you move from Regex.Replace to JavaScript's replace — same captures, same values.