Menu

Regex w C#: Match, Replace, Split i grupy z przykładami

Jak używać wyrażeń regularnych w C# z System.Text.RegularExpressions: IsMatch, Match i Matches, grupy numerowane i nazwane, Replace z podstawieniami i lambdą, Split, RegexOptions, stringi dosłowne dla wzorców, walidacja danych, limity czasu i atrybut GeneratedRegex z .NET 7.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Wyrażenie regularne (regex) to wzorzec opisujący tekst: "cztery cyfry, myślnik, dwie cyfry", "słowo zaczynające się wielką literą", "cokolwiek między nawiasami kwadratowymi". W C# klasa Regex z System.Text.RegularExpressions wyszukuje, wyciąga, zamienia i dzieli tekst przy użyciu takich wzorców.

IsMatch, Match i Matches

Trzy metody obsługują większość wyszukiwań. IsMatch odpowiada tak lub nie, Match zwraca pierwsze dopasowanie, a Matches wszystkie:

Wynik:

True
2026-03-14 at index 22
4 matches:
  1042
  2026
  1043
  2026
Success: False, Value: []

Nieudane Match nie zwraca null: zwraca Match, którego Success ma wartość false, a Value jest puste. Zawsze sprawdzaj Success przed użyciem dopasowania.

Wyszukiwanie czterech cyfr pokazuje, jak wzorce łapią więcej, niż zamierzano. \b\d{4}\b oznacza "dokładnie cztery cyfry jako całe słowo", a lata w datach się kwalifikują, bo - to granica słowa. Zawęź wzorzec, żeby mówił dokładnie to, o co ci chodzi, na przykład (?<=Order |order )\d+, albo użyj grup, o których dalej.

Stringi dosłowne dla wzorców

Składnia regex wszędzie używa ukośników wstecznych (\d, \s, \b), podobnie jak sekwencje ucieczki w stringach C#. W zwykłym stringu "\d" nawet się nie kompiluje (CS1009, nierozpoznana sekwencja ucieczki), a "\b" kompiluje się do znaku backspace i po cichu zmienia wzorzec. Zapisuj wzorce jako stringi dosłowne z @, w których ukośnik wsteczny to po prostu ukośnik wsteczny:

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

W stringu dosłownym cudzysłów zapisuje się jako "". Surowe literały stringów z C# 11 ("""...""") pozwalają uniknąć nawet tego.

Grupy: wyciąganie części dopasowania

Nawiasy tworzą grupę, a tekst każdej grupy jest dostępny po dopasowaniu. Grupy są numerowane od 1 w kolejności nawiasów otwierających; grupa 0 to całe dopasowanie. Grupy nazwane, (?<name>...), łatwiej czytać i przetrwają zmiany we wzorcu:

Wynik:

2026-03-14
ERROR
payments
Card declined for order 1042
60 by 90, area 5400

[ i ] mają w regex znaczenie (klasa znaków), więc dopasowanie dosłownych nawiasów wymaga \[ i \]. To samo dotyczy . * + ? ( ) { } ^ $ | \. Żeby dopasować dosłownie string podany przez użytkownika wewnątrz wzorca, przepuść go przez Regex.Escape, który sam poprzedza metaznaki ukośnikami: Regex.Escape("price (USD)") zwraca price\ \(USD\) (spacje też są poprzedzone ukośnikiem, co jest nieszkodliwe).

Nawiasy, które tylko grupują, bez przechwytywania, zapisuje się jako (?:...). Utrzymują porządek w numeracji grup i są nieco szybsze.

Replace: podstawienia i lambdy

Regex.Replace zastępuje każde dopasowanie. W tekście zastępczym $1 wstawia grupę 1, ${name} grupę nazwaną, a $0 całe dopasowanie. Dla wszystkiego, czego szablon nie wyrazi, przekaż funkcję (zwykle lambdę), która dostaje każdy Match i zwraca jego zamiennik:

Wynik:

Invoiced 14/03/2026, paid 02/04/2026.
too many spaces
Card **** **** **** 1234
Tea 2.75, Scone 3.52, Jam 1.10

Maska karty używa lookahead, (?=...): dopasowuje cyfrę tylko wtedy, gdy następują po niej jeszcze cztery cyfry, bez ich konsumowania. Asercje otoczenia ((?=...), (?!...), (?<=...), (?<!...)) sprawdzają kontekst bez włączania go do dopasowania, i właśnie dlatego jedno Replace może zostawić ostatnie cztery cyfry nietknięte.

Dla zwykłego tekstu bez wzorca string.Replace jest prostsze i szybsze; po Regex.Replace sięgaj wtedy, gdy to, co trzeba zastąpić, się zmienia.

Split

Regex.Split dzieli tekst przy każdym dopasowaniu wzorca, co radzi sobie z różnymi separatorami:

Wynik:

csharp / dotnet / regex / tutorial / beginner
Order Shipped Event Handler

Jeśli wzorzec zawiera grupy przechwytujące, Regex.Split dołącza przechwycone separatory do wyniku; użyj (?:...), gdy ich nie chcesz.

RegexOptions

Opcje zmieniają zachowanie całego wzorca. Łącz je przez |:

Wynik:

1
2
error INFO Error
True

Te, których będziesz używać:

  • IgnoreCase: dopasowanie bez rozróżniania wielkości liter (forma wewnątrz wzorca: (?i)).
  • Multiline: ^ i $ pasują na początku i końcu każdej linii, a nie tylko całego stringu.
  • Singleline: . pasuje też do \n (domyślnie pasuje do każdego znaku poza znakiem nowej linii).
  • IgnorePatternWhitespace: spacje we wzorcu są ignorowane, a # rozpoczyna komentarz, co przydaje się przy długich wzorcach pisanych w kilku liniach.
  • CultureInvariant: z IgnoreCase porównuje bez reguł bieżącej kultury.
  • Compiled: kompiluje wzorzec raz do IL, dla regexa używanego wiele razy.

Walidacja danych: kotwice i kwestia e-maila

Przy walidacji zakotwicz wzorzec przez ^ i $, inaczej przejdzie, gdy tylko część danych pasuje:

Wynik:

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

Wzorzec e-maila sprawdza tylko kształt: coś bez spacji ani @, jedno @, domena z kropką. To celowe. Prawdziwa gramatyka adresów (RFC 5322) dopuszcza części lokalne w cudzysłowach, komentarze i domeny w postaci adresów IP; wzorce, które próbują ją pokryć, rozrastają się do setek znaków i nadal odrzucają poprawne adresy, których ludzie naprawdę używają. A adres idealny składniowo i tak może zostać odrzucony przy wysyłce. Sprawdź kształt, a potem wyślij e-mail z potwierdzeniem. System.Net.Mail.MailAddress oferuje inne sprawdzenie kształtu, jeśli wolisz nie pisać wzorca.

Wydajność: metody statyczne, instancje, kompilacja

Metody statyczne (Regex.IsMatch(input, pattern)) parsują wzorzec i trzymają go w małym cache (domyślnie 15 wzorców), więc powtarzane wywołania z tym samym wzorcem są tanie. Dla wzorca używanego w gorącej pętli utwórz jedną instancję Regex i trzymaj ją w polu static readonly; dodaj RegexOptions.Compiled, jeśli wykonuje się tysiące razy, wymieniając wolniejszy start na szybsze dopasowywanie.

Limity czasu i katastrofalne nawroty

Niektóre wzorce na pewnych danych zajmują czas wykładniczy. Klasyka to zagnieżdżony kwantyfikator, na przykład ^(a+)+$ wobec "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": silnik próbuje każdego sposobu podziału liter a między dwa +, zanim się podda. Na serwerze webowym jedno takie wejście od użytkownika może zająć cały rdzeń procesora (atak "ReDoS"). Każdemu regexowi, który obsługuje niezaufane dane, nadaj limit czasu:

var pattern = new Regex(@"^(\w+\s?)*$", RegexOptions.None, TimeSpan.FromMilliseconds(100));
try
{
    bool ok = pattern.IsMatch(userInput);
}
catch (RegexMatchTimeoutException)
{
    // treat as invalid input
}

Każda metoda statyczna ma też przeciążenie przyjmujące limit czasu. .NET 7 dodał również RegexOptions.NonBacktracking, silnik gwarantujący czas liniowy kosztem niektórych funkcji (asercji otoczenia, odwołań wstecznych).

GeneratedRegex (.NET 7)

.NET 7 dodał generator źródeł, który w czasie kompilacji zamienia wzorzec w zwykły kod C#. Dostajesz szybkość Compiled bez kosztu przy starcie, wygenerowany kod da się przeczytać w IDE, a całość działa z trimmingiem i 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

W .NET 7 i nowszych to zalecana forma dla każdego wzorca znanego w czasie kompilacji.

Typowe błędy

  • Brak kotwic przy walidacji. Bez ^...$ IsMatch akceptuje dane, które jedynie zawierają dopasowanie.
  • Zwykłe stringi dla wzorców. "\b" to backspace, a nie granica słowa. Używaj @"...".
  • Niepoprzedzone ukośnikiem znaki specjalne. . pasuje do dowolnego znaku; 3.50 jako wzorzec pasuje też do 3x50. Pisz \. albo użyj Regex.Escape dla danych od użytkownika.
  • Zachłanne kwantyfikatory. <.*> na <b>bold</b> dopasowuje cały string. Użyj leniwego .*? albo zanegowanej klasy [^>]*.
  • Brak limitu czasu dla niezaufanych danych. Zagnieżdżony kwantyfikator może zawiesić żądanie.
  • Regex do formatów strukturalnych. HTML, JSON i CSV z cudzysłowami wymagają parsera, a nie wzorca.

Najczęściej zadawane pytania

Jak używać regex w C#?

Dodaj using System.Text.RegularExpressions; i wywołuj statyczne metody Regex: Regex.IsMatch(input, pattern) zwraca bool, Regex.Match zwraca pierwsze dopasowanie, Regex.Matches wszystkie, Regex.Replace podstawia, a Regex.Split dzieli. Zapisuj wzorce jako stringi dosłowne, @"\d+", żeby ukośniki wsteczne trafiały do silnika regex bez zmian.

Jak pobrać wartość grupy z dopasowania regex w C#?

Otocz nawiasami interesującą cię część i odczytaj match.Groups[1].Value (grupy są numerowane od 1; grupa 0 to całe dopasowanie). Grupy nazwane są czytelniejsze: (?<year>\d{4}) odczytuje się przez match.Groups["year"].Value. Najpierw sprawdź match.Success, bo nieudane dopasowanie ma puste grupy, a nie null.

Jak zamienić tekst za pomocą regex w C#?

Regex.Replace(input, pattern, replacement) zastępuje każde dopasowanie. Tekst zastępczy może odwoływać się do grup: $1 dla grupy numerowanej, ${name} dla nazwanej, $0 dla całego dopasowania. Dla logiki, której szablon nie wyrazi, przekaż lambdę: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

Jak sprawić, by regex w C# ignorował wielkość liter?

Przekaż RegexOptions.IgnoreCase: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). Opcje łączy się przez |, na przykład RegexOptions.IgnoreCase | RegexOptions.Multiline. Możesz też włączyć tę opcję wewnątrz wzorca przez (?i).

Jak zwalidować adres e-mail za pomocą regex w C#?

Wzorzec w rodzaju ^[^@\s]+@[^@\s]+\.[^@\s]+$ wyłapuje oczywiste literówki (brak @, spacje, brak kropki w domenie) i zwykle wystarcza w formularzu. Regex nie potrafi naprawdę zwalidować adresu: pełna gramatyka dopuszcza formy, których nie obsłuży żaden praktyczny wzorzec, a poprawny składniowo adres może nie istnieć. Sprawdź podstawowy kształt, a potem potwierdź adres, wysyłając e-mail weryfikacyjny.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ