Download Cheat sheet PDF 12 pages · syntax, editors, patterns, Unicode, performance, debugging
Guide

Regex in C# / .NET

.NET has the most feature-rich regex engine of any mainstream language. Includes lookarounds, balancing groups, conditionals, atomic groups, and Unicode properties.

The basics

using System.Text.RegularExpressions;

// Static methods — compile-on-demand and cache
bool ok = Regex.IsMatch(text, @"\d+");
Match m = Regex.Match(text, @"(\d+)-(\d+)");
MatchCollection all = Regex.Matches(text, @"\d+");
string result = Regex.Replace(text, @"\d+", "#");

// Or create a Regex instance for repeated use
var re = new Regex(@"(\d+)-(\d+)", RegexOptions.Compiled);
Match match = re.Match(text);

The @"..." verbatim string syntax lets you write backslashes naturally — no double-escaping needed.

RegexOptions.Compiled

By default .NET interprets regex bytecode. With RegexOptions.Compiled, it generates IL at first use — slower startup, faster steady-state. Use it for hot-path regexes used many times.

From .NET 7+, you can also use source generation with [GeneratedRegex]:

partial class Parser
{{
    [GeneratedRegex(@"(\d+)-(\d+)")]
    private static partial Regex DateRegex();

    public void Parse(string text)
    {{
        Match m = DateRegex().Match(text);
    }}
}}

Compiles the regex at build time. Fastest option for known patterns.

Named groups

.NET supports both (?<name>...) and (?'name'...):

var re = new Regex(@"(?<year>\d{{4}})-(?<month>\d{{2}})");
var m = re.Match("2024-06");
string year = m.Groups["year"].Value;

Group accessor returns a Group object. Success tells you if it participated; Captures contains all captures if the group was inside a quantifier (.NET tracks them all, unlike most engines).

Replacements with named groups

string result = Regex.Replace(
    "first-last",
    @"(?<first>\w+)-(?<last>\w+)",
    "${{last}} ${{first}}"
);
// "last first"

Use ${{name}} in the replacement, not $<name>.

RegexOptions flags

RegexOptions.IgnoreCase
RegexOptions.Multiline
RegexOptions.Singleline      // dot matches newline
RegexOptions.IgnorePatternWhitespace  // verbose mode
RegexOptions.Compiled
RegexOptions.ECMAScript      // JS-compatible matching semantics
RegexOptions.CultureInvariant
RegexOptions.RightToLeft     // match RTL — useful for some HR/Arabic processing
RegexOptions.NonBacktracking // .NET 7+ — switch to RE2-like linear-time engine

The new NonBacktracking option is huge: opt into linear-time matching for any pattern that doesn't use back-references or atomic groups. Trades off some features for guaranteed performance.

Match timeout

.NET is unique in offering a per-match timeout, so a runaway regex can't hang your process:

var re = new Regex(@"(a+)+$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{{
    re.IsMatch(input);
}}
catch (RegexMatchTimeoutException)
{{
    // ReDoS detected, handled safely
}}

You can also set a process-wide default: AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromSeconds(1));.

Unique .NET features

Balancing groups — match nested parens or brackets in a single regex, something most engines can't do natively.

Variable-length lookbehind — most engines require fixed-width lookbehind. .NET allows arbitrary length.

Right-to-left matching — set RegexOptions.RightToLeft and the engine matches starting from the end. Useful for RTL languages and certain parsing tasks.

Conditional patterns(?(1)yes|no) matches differently based on whether group 1 captured.

If you need any of these, .NET's the only mainstream engine that handles them out of the box.


← Back to guides