Regex Tester
Test regex patterns against sample input and review highlighted matches in real time.
Regex in .NET: where I actually use it
The two places where I write regex in ASP.NET Core projects most often are validation attributes and route constraints. A [RegularExpression] data annotation on a DTO property runs during model binding and returns a 400 with a structured error if it fails. A regex route constraint like {id:regex(^[0-9]{{4}}$)} prevents certain routes from matching entirely before they reach the controller action. Both need to be right the first time — writing them blind and only finding out from test failures is slow.
A field note: the regex bugs that actually reach production are rarely exotic — they're a missing anchor. Drop the ^ and $ from a validation pattern and Regex.IsMatch stops requiring the whole input to match; it's satisfied by finding the pattern anywhere inside a longer string, so a value like "junk-AB-1234-more junk" passes a check meant to accept only "AB-1234". Testing a pattern against strings you expect to be rejected, not just ones you expect to be accepted, is what catches this before it ships.
How to test a regex pattern online
- Enter your regex pattern in the top editor — no leading or trailing slashes (use the flag checkboxes instead).
- Paste your sample text in the lower editor.
- Matches highlight automatically. Enable "Case insensitive" or "Multiline" flags as needed.
- Review match count, index, and length metadata for each match in the results panel.
Anatomy of a regex pattern
Breaking the SKU pattern from the code sample below into its parts:
Essential regex syntax reference
- . — any single character except newline; \s — whitespace; \d — digit; \w — word character
- ^ — start of line (or string without multiline flag); $ — end of line
- * — zero or more; + — one or more; ? — zero or one; {n,m} — between n and m repetitions
- [abc] — character class; [^abc] — negated class; [a-z] — range
- (group) — capturing group; (?:group) — non-capturing; (?<name>group) — named capturing
- (?=ahead) — positive lookahead; (?!ahead) — negative lookahead; (?<=behind) — positive lookbehind
The .NET regex engine: what's different
This tester runs .NET's System.Text.RegularExpressions.Regex engine on the server, which means your pattern results here match exactly what you'll get in your C# code. The .NET engine has capabilities that JavaScript and Python regex don't:
- Named capturing groups:
(?<name>...)— access matched groups by name withmatch.Groups["name"].Value - Variable-length lookbehinds:
(?<=\d{2,4})-works in .NET; JavaScript only allows fixed-length lookbehinds - Atomic groups:
(?>...)— prevents backtracking into the group, critical for eliminating catastrophic backtracking in complex patterns - Balancing groups:
(?<open>...) ... (?<-open>...)— match nested structures like balanced parentheses
.NET Regex vs JavaScript RegExp
| Feature | .NET (System.Text.RegularExpressions) | JavaScript (RegExp) |
|---|---|---|
| Named capturing groups | (?<name>...) via match.Groups["name"] | (?<name>...) via match.groups.name (ES2018+) |
| Inline flags mid-pattern | (?i), (?s), (?x) supported inline, anywhere in the pattern | Not supported — flags are set on the RegExp object/literal only |
| Atomic groups | (?>...) — prevents backtracking into the group | No native syntax |
| Balancing groups | (?<name>...)...(?<-name>...) — match balanced/nested constructs | No equivalent |
| Default multi-match behavior | Regex.Matches() returns every match by default | Requires the g flag on the RegExp to get multiple matches via matchAll() |
// DTO property validation with a regex attribute
public class CreateProductRequest
{
[Required]
[RegularExpression(@"^[A-Z]{2}-\d{4,8}$",
ErrorMessage = "SKU must be two uppercase letters, a hyphen, and 4–8 digits.")]
public string Sku { get; set; } = string.Empty;
}
// Named groups for structured parsing
var pattern = @"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})";
var match = Regex.Match("2024-03-15", pattern);
if (match.Success)
{
int year = int.Parse(match.Groups["year"].Value);
int month = int.Parse(match.Groups["month"].Value);
int day = int.Parse(match.Groups["day"].Value);
}
// Compiled regex as a static field for hot paths (avoids repeated compilation)
public static partial class Patterns
{
[GeneratedRegex(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")]
public static partial Regex Email();
}
In .NET 7+, the source-generated [GeneratedRegex] attribute compiles the pattern at build time instead of at runtime. For regex patterns used on hot paths (validation in a request pipeline, parsing in a high-throughput service), this eliminates the startup compilation cost.
Five real .NET debugging scenarios
- Writing a
[RegularExpression]validation attribute for a product SKU or account number format. Test the pattern here against real and invalid examples before putting it in the DTO — the error message from model binding doesn't include which character failed. - Validating an ASP.NET Core route constraint. Route patterns like
{id:regex(^[0-9]{{4}}$)}use doubled braces in the route template. Test the pattern itself (without the doubling) here to confirm it matches expected path segments before adding the constraint. - Parsing log lines from a structured log file in a .NET tool or background job. Use named groups to extract fields from log entries and build typed DTOs without a full log parsing library.
- Extracting values from an HTTP response body that isn't JSON — legacy SOAP responses, HTML scraping for internal tools, or fixed-width report formats. Regex with named groups produces cleaner extraction code than string splits.
- Writing a custom
IModelBinderor input formatter that needs to detect the format of an incoming value. Test the detection pattern here with real inputs from your target system before integrating it into the binder.
Frequently asked questions
Why do I need to double the backslash in C# regex strings? C# string literals use \ as an escape character, so \d in a regular string literal is a compiler escape sequence (invalid), not a regex metacharacter. Use verbatim strings: @"\d+" passes the literal characters \d+ to the regex engine. This is the most common source of regex-compiles-but-doesn't-match bugs in .NET code.
What is RegexOptions.Compiled vs [GeneratedRegex]? RegexOptions.Compiled compiles the pattern to IL at runtime on first use, trading startup cost for faster subsequent matches. [GeneratedRegex] (.NET 7+) compiles the pattern at build time — zero runtime overhead. For patterns used more than a few times per request, generated regex is the recommended approach in modern .NET.
How do I use regex in an ASP.NET Core route constraint? Use the regex constraint: {slug:regex(^[a-z0-9-]+$)}. Curly braces inside the regex pattern must be doubled: {id:regex(^\d{{4,8}}$)} because the route template parser uses { and } as delimiters.
Catastrophic backtracking
Patterns with nested quantifiers like (a+)+ cause exponential backtracking — the engine explores every possible combination before giving up. This tool enforces a 5-second timeout on every pattern test using the Regex constructor's matchTimeout parameter. In your production code, always pass a timeout: new Regex(pattern, options, TimeSpan.FromSeconds(2)) or use RegexOptions.NonBacktracking (.NET 7+) for patterns that must run in linear time.
Regex pattern
\b[A-Z][a-z]+\b
Sample text
Alice and Bob built DevToolsHub in Blazor.
Live match preview
Alice and Bob built DevToolsHub in Blazor.
Regex matches
Match metadata
[
{
"Value": "Alice",
"Index": 0,
"Length": 5
},
{
"Value": "Bob",
"Index": 10,
"Length": 3
},
{
"Value": "Blazor",
"Index": 35,
"Length": 6
}
]