Ein regulärer Ausdruck (Regex) ist ein Muster, das Text beschreibt: „vier Ziffern, ein Bindestrich, zwei Ziffern“, „ein Wort, das mit einem Großbuchstaben beginnt“, „alles zwischen eckigen Klammern“. In C# findet, extrahiert, ersetzt und teilt die Klasse Regex in System.Text.RegularExpressions Text mit solchen Mustern.
IsMatch, Match und Matches
Drei Methoden decken die meisten Suchen ab. IsMatch antwortet mit Ja oder Nein, Match gibt den ersten Treffer zurück und Matches alle:
Ausgabe:
True
2026-03-14 at index 22
4 matches:
1042
2026
1043
2026
Success: False, Value: []
Ein gescheitertes Match gibt nicht null zurück: Es gibt ein Match zurück, dessen Success false und dessen Value leer ist. Prüfe immer Success, bevor du einen Treffer verwendest.
Die Suche nach vier Ziffern zeigt, wie Muster mehr erfassen als beabsichtigt. \b\d{4}\b bedeutet „genau vier Ziffern als ganzes Wort“, und die Jahre in den Datumswerten passen, weil - eine Wortgrenze ist. Verschärfe das Muster, damit es sagt, was du meinst, zum Beispiel (?<=Order |order )\d+, oder nimm Gruppen, siehe nächster Abschnitt.
Verbatim-Strings für Muster
Die Regex-Syntax verwendet überall Backslashes (\d, \s, \b), und die Escape-Sequenzen von C#-Strings ebenfalls. In einem normalen String kompiliert "\d" nicht einmal (CS1009, unrecognized escape sequence), und "\b" kompiliert zu einem Rückschrittzeichen, was das Muster stillschweigend ändert. Schreibe Muster als Verbatim-Strings mit @, in denen ein Backslash einfach ein Backslash ist:
var a = new Regex("\\d+\\.\\d{2}"); // escaped twice: hard to read
var b = new Regex(@"\d+\.\d{2}"); // verbatim: what the regex engine sees
In einem Verbatim-String wird ein doppeltes Anführungszeichen als "" geschrieben. Raw-String-Literale aus C# 11 ("""...""") vermeiden sogar das.
Gruppen: Teile eines Treffers extrahieren
Klammern erzeugen eine Gruppe, und der Text jeder Gruppe steht nach dem Treffer zur Verfügung. Gruppen werden ab 1 nummeriert, in der Reihenfolge ihrer öffnenden Klammer; Gruppe 0 ist der ganze Treffer. Benannte Gruppen, (?<name>...), sind leichter zu lesen und überstehen Änderungen am Muster:
Ausgabe:
2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400
[ und ] haben in einer Regex eine Bedeutung (eine Zeichenklasse), um die wörtlichen Klammern zu treffen, braucht es also \[ und \]. Dasselbe gilt für . * + ? ( ) { } ^ $ | \. Um einen vom Benutzer gelieferten String wörtlich in einem Muster zu treffen, schicke ihn durch Regex.Escape, das die Metazeichen für dich escaped: Regex.Escape("price (USD)") gibt price\ \(USD\) zurück (auch Leerzeichen werden escaped, was harmlos ist).
Klammern, die nur gruppieren, ohne zu erfassen, werden (?:...) geschrieben. Sie halten die Nummerierung der Gruppen sauber und sind etwas schneller.
Replace: Ersetzungsmuster und Lambdas
Regex.Replace ersetzt jeden Treffer. Im Ersatzstring fügt $1 Gruppe 1 ein, ${name} eine benannte Gruppe und $0 den ganzen Treffer. Für alles, was eine Vorlage nicht ausdrücken kann, übergib eine Funktion (meist ein Lambda), die jedes Match bekommt und seinen Ersatz zurückgibt:
Ausgabe:
Invoiced 14/03/2026, paid 02/04/2026.
too many spaces
Card **** **** **** 1234
Tea 2.75, Scone 3.52, Jam 1.10
Die Kartenmaske verwendet einen Lookahead, (?=...): Er trifft eine Ziffer nur, wenn vier weitere Ziffern folgen, ohne diese zu verbrauchen. Lookarounds ((?=...), (?!...), (?<=...), (?<!...)) prüfen den Kontext, ohne ihn in den Treffer aufzunehmen, und genau das lässt ein einziges Replace die letzten vier Ziffern unangetastet lassen.
Für einfachen Text ohne Muster ist string.Replace einfacher und schneller; greif zu Regex.Replace, wenn das zu Ersetzende variiert.
Split
Regex.Split teilt an jedem Treffer eines Musters, was mit wechselnden Trennzeichen umgehen kann:
Ausgabe:
csharp / dotnet / regex / tutorial / beginner
Order Shipped Event Handler
Enthält das Muster erfassende Gruppen, nimmt Regex.Split die erfassten Trennzeichen ins Ergebnis auf; nimm (?:...), wenn du sie nicht willst.
RegexOptions
Optionen ändern, wie sich das ganze Muster verhält. Kombiniere sie mit |:
Ausgabe:
1
2
error INFO Error
True
Die, die du verwenden wirst:
IgnoreCase: Abgleich ohne Beachtung der Schreibweise (Inline-Form(?i)).Multiline:^und$treffen am Anfang und Ende jeder Zeile, nicht nur des ganzen Strings.Singleline:.trifft auch\n(standardmäßig trifft es jedes Zeichen außer einem Zeilenumbruch).IgnorePatternWhitespace: Leerzeichen im Muster werden ignoriert, und#beginnt einen Kommentar, für lange Muster über mehrere Zeilen.CultureInvariant: mitIgnoreCaseohne die Regeln der aktuellen Kultur vergleichen.Compiled: das Muster einmal in IL kompilieren, für eine Regex, die oft verwendet wird.
Eingaben validieren: Anker und die Frage nach der E-Mail
Verankere das Muster zur Validierung mit ^ und $, sonst ist es erfüllt, sobald ein Teil der Eingabe passt:
Ausgabe:
PT-1000 loose=True strict=True
pt-1000 loose=False strict=False
XPT-1000Y loose=True strict=False
PT-10 loose=False strict=False
ana@example.com True
ana@example False
ana @example.com False
ana@@example.com False
Das E-Mail-Muster prüft nur die Form: etwas ohne Leerzeichen oder @, ein @, eine Domain mit einem Punkt. Das ist Absicht. Die echte Grammatik für Adressen (RFC 5322) erlaubt Local Parts in Anführungszeichen, Kommentare und Domains als IP-Literale; Muster, die das abdecken wollen, werden hunderte Zeichen lang und lehnen trotzdem gültige Adressen ab, die Menschen wirklich verwenden. Und eine syntaktisch perfekte Adresse kann trotzdem unzustellbar sein. Prüfe die Form und schicke dann eine Bestätigungs-E-Mail. System.Net.Mail.MailAddress bietet eine weitere Formprüfung, wenn du lieber kein Muster schreibst.
Performance: statische Methoden, Instanzen, Kompilierung
Die statischen Methoden (Regex.IsMatch(input, pattern)) parsen das Muster und halten es in einem kleinen Cache (standardmäßig 15 Muster), wiederholte Aufrufe mit demselben Muster sind also günstig. Für ein Muster in einer heißen Schleife erzeuge eine Regex-Instanz und halte sie in einem static readonly-Feld; ergänze RegexOptions.Compiled, wenn sie tausende Male läuft, und tausche einen langsameren Start gegen schnelleren Abgleich.
Timeouts und katastrophales Backtracking
Manche Muster brauchen bei bestimmten Eingaben exponentielle Zeit. Der Klassiker ist ein verschachtelter Quantor wie ^(a+)+$ gegen "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": Die Engine probiert jede Aufteilung der as zwischen den beiden + durch, bevor sie aufgibt. Auf einem Webserver kann eine einzige solche Eingabe eines Benutzers einen CPU-Kern blockieren (ein „ReDoS“-Angriff). Gib jeder Regex, die nicht vertrauenswürdige Eingaben verarbeitet, einen Timeout:
var pattern = new Regex(@"^(\w+\s?)*$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{
bool ok = pattern.IsMatch(userInput);
}
catch (RegexMatchTimeoutException)
{
// treat as invalid input
}
Jede statische Methode hat ebenfalls eine Überladung mit Timeout. .NET 7 hat außerdem RegexOptions.NonBacktracking hinzugefügt, eine Engine, die lineare Zeit garantiert, dafür aber auf einige Features verzichtet (Lookarounds, Rückverweise).
GeneratedRegex (.NET 7)
.NET 7 hat einen Source Generator hinzugefügt, der ein Muster zur Kompilierzeit in gewöhnlichen C#-Code verwandelt. Du bekommst die Geschwindigkeit von Compiled ohne Startkosten, der erzeugte Code ist in der IDE lesbar, und er funktioniert mit Trimming und Native AOT:
public static partial class Patterns
{
[GeneratedRegex(@"^[A-Z]{2}-\d{4}$", RegexOptions.IgnoreCase)]
public static partial Regex ProductCode();
}
bool ok = Patterns.ProductCode().IsMatch("pt-1000"); // True
Ab .NET 7 ist das die empfohlene Form für jedes Muster, das zur Kompilierzeit bekannt ist.
Häufige Fehler
- Fehlende Anker bei der Validierung. Ohne
^...$akzeptiertIsMatchEingaben, die nur einen Treffer enthalten. - Normale Strings für Muster.
"\b"ist ein Rückschrittzeichen, keine Wortgrenze. Nimm@"...". - Nicht escapete Sonderzeichen.
.trifft jedes Zeichen;3.50als Muster trifft auch3x50. Escape mit\.oder bei Benutzereingaben mitRegex.Escape. - Gierige Quantoren.
<.*>auf<b>bold</b>trifft den ganzen String. Nimm das nicht gierige.*?oder eine negierte Klasse[^>]*. - Kein Timeout bei nicht vertrauenswürdigen Eingaben. Ein verschachtelter Quantor kann eine Anfrage hängen lassen.
- Regex für strukturierte Formate. HTML, JSON und CSV mit Anführungszeichen brauchen einen Parser, kein Muster.
Häufig gestellte Fragen
Wie verwende ich Regex in C#?
Füge using System.Text.RegularExpressions; hinzu und rufe die statischen Methoden von Regex auf: Regex.IsMatch(input, pattern) gibt ein bool zurück, Regex.Match den ersten Treffer, Regex.Matches alle, Regex.Replace ersetzt und Regex.Split teilt. Schreibe Muster als Verbatim-Strings, @"\d+", damit Backslashes unverändert bei der Regex-Engine ankommen.
Wie bekomme ich in C# den Wert einer Gruppe aus einem Regex-Treffer?
Setze Klammern um den gewünschten Teil und lies match.Groups[1].Value (Gruppen werden ab 1 nummeriert; Gruppe 0 ist der ganze Treffer). Benannte Gruppen sind klarer: (?<year>\d{4}) wird mit match.Groups["year"].Value gelesen. Prüfe zuerst match.Success, denn ein gescheiterter Treffer hat leere Gruppen statt null.
Wie ersetze ich in C# Text mit Regex?
Regex.Replace(input, pattern, replacement) ersetzt jeden Treffer. Der Ersatz kann auf Gruppen verweisen: $1 für eine nummerierte Gruppe, ${name} für eine benannte, $0 für den ganzen Treffer. Für Logik, die eine Vorlage nicht ausdrücken kann, übergib ein Lambda: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).
Wie mache ich eine C#-Regex unabhängig von Groß- und Kleinschreibung?
Übergib RegexOptions.IgnoreCase: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). Optionen lassen sich mit | kombinieren, zum Beispiel RegexOptions.IgnoreCase | RegexOptions.Multiline. Du kannst es auch im Muster mit (?i) einschalten.
Wie validiere ich in C# eine E-Mail-Adresse mit Regex?
Ein Muster wie ^[^@\s]+@[^@\s]+\.[^@\s]+$ erkennt offensichtliche Tippfehler (fehlendes @, Leerzeichen, kein Punkt in der Domain) und ist meist alles, was ein Formular braucht. Eine Regex kann eine Adresse nicht wirklich validieren: Die vollständige Grammatik erlaubt Formen, die kein praktisches Muster abdeckt, und eine syntaktisch gültige Adresse muss nicht existieren. Prüfe die grobe Form und bestätige dann mit einer Verifizierungs-E-Mail.