Menu

Регулярные выражения в C#: Regex, Match, Replace, Split и группы

Как использовать регулярные выражения в C# через System.Text.RegularExpressions: IsMatch, Match и Matches, нумерованные и именованные группы, Replace с подстановками и лямбдой, Split, RegexOptions, verbatim-строки для шаблонов, проверка ввода, тайм-ауты и атрибут GeneratedRegex из .NET 7.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Регулярное выражение (regex) это шаблон, описывающий текст: «четыре цифры, дефис, две цифры», «слово, начинающееся с заглавной буквы», «всё между квадратными скобками». В C# класс Regex из System.Text.RegularExpressions находит, извлекает, заменяет и разбивает текст по таким шаблонам.

IsMatch, Match и Matches

Три метода покрывают большинство поисков. IsMatch отвечает да или нет, Match возвращает первое совпадение, а Matches все:

Вывод:

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

Неудачный Match не возвращает null: он возвращает Match, у которого Success равно false, а Value пустое. Всегда проверяйте Success, прежде чем использовать совпадение.

Поиск четырёх цифр показывает, как шаблоны захватывают больше задуманного. \b\d{4}\b означает «ровно четыре цифры как целое слово», и годы внутри дат подходят, потому что - это граница слова. Уточните шаблон, чтобы он говорил то, что вы имеете в виду, например (?<=Order |order )\d+, или используйте группы, о них дальше.

Verbatim-строки для шаблонов

Синтаксис регулярных выражений повсюду использует обратные слеши (\d, \s, \b), как и escape-последовательности строк C#. В обычной строке "\d" даже не компилируется (CS1009, unrecognized escape sequence), а "\b" компилируется в символ возврата на шаг, молча меняя шаблон. Записывайте шаблоны как verbatim-строки с @, где обратный слеш это просто обратный слеш:

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

В verbatim-строке двойная кавычка записывается как "". Необработанные строковые литералы C# 11 ("""...""") избавляют даже от этого.

Группы: извлечение частей совпадения

Скобки создают группу, и текст каждой группы доступен после совпадения. Группы нумеруются с 1 в порядке открывающих скобок; группа 0 это всё совпадение. Именованные группы, (?<name>...), легче читать, и они переживают правки шаблона:

Вывод:

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

У [ и ] в регулярных выражениях есть смысл (класс символов), поэтому для совпадения с буквальными скобками нужны \[ и \]. То же касается . * + ? ( ) { } ^ $ | \. Чтобы сопоставить внутри шаблона строку, введённую пользователем, буквально, пропустите её через Regex.Escape, который экранирует метасимволы за вас: Regex.Escape("price (USD)") возвращает price\ \(USD\) (пробелы тоже экранируются, что безвредно).

Скобки, которые только группируют, ничего не захватывая, записываются как (?:...). Они сохраняют нумерацию групп чистой и работают немного быстрее.

Replace: подстановки и лямбды

Regex.Replace заменяет каждое совпадение. В строке замены $1 вставляет группу 1, ${name} именованную группу, а $0 всё совпадение. Для всего, что шаблон замены не может выразить, передайте функцию (обычно лямбду), которая получает каждый Match и возвращает замену для него:

Вывод:

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

Маска карты использует просмотр вперёд, (?=...): он совпадает с цифрой, только если за ней следуют ещё четыре цифры, не поглощая их. Просмотры ((?=...), (?!...), (?<=...), (?<!...)) проверяют контекст, не включая его в совпадение, и именно это позволяет одному Replace сохранить последние четыре цифры нетронутыми.

Для простого текста без шаблона string.Replace проще и быстрее; берите Regex.Replace, когда заменяемое меняется.

Split

Regex.Split разбивает строку по каждому совпадению шаблона, что справляется с меняющимися разделителями:

Вывод:

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

Если в шаблоне есть захватывающие группы, Regex.Split включает захваченные разделители в результат; используйте (?:...), когда они не нужны.

RegexOptions

Параметры меняют поведение всего шаблона. Объединяйте их через |:

Вывод:

1
2
error INFO Error
True

Те, которые вам понадобятся:

  • IgnoreCase: сопоставление без учёта регистра (встроенная форма (?i)).
  • Multiline: ^ и $ совпадают с началом и концом каждой строки, а не только всей строки текста.
  • Singleline: . совпадает также с \n (по умолчанию он совпадает с любым символом, кроме перевода строки).
  • IgnorePatternWhitespace: пробелы в шаблоне игнорируются, а # начинает комментарий, для длинных шаблонов, записанных в несколько строк.
  • CultureInvariant: вместе с IgnoreCase сравнивает без правил текущей культуры.
  • Compiled: один раз компилирует шаблон в IL, для регулярного выражения, которое используется много раз.

Проверка ввода: якоря и вопрос email

Для проверки привязывайте шаблон якорями ^ и $, иначе он пропустит ввод, как только совпадёт часть его:

Вывод:

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

Шаблон email проверяет только форму: что-то без пробелов и @, один @, домен с точкой. Это сделано намеренно. Настоящая грамматика адресов (RFC 5322) допускает локальные части в кавычках, комментарии и домены в виде IP-адресов; шаблоны, которые пытаются её охватить, растягиваются на сотни символов и всё равно отвергают корректные адреса, которыми люди действительно пользуются. А синтаксически идеальный адрес всё равно может вернуть письмо. Проверьте форму, затем отправьте письмо с подтверждением. System.Net.Mail.MailAddress предлагает ещё одну проверку формы, если вы предпочитаете не писать шаблон.

Производительность: статические методы, экземпляры, компиляция

Статические методы (Regex.IsMatch(input, pattern)) разбирают шаблон и держат его в небольшом кэше (по умолчанию 15 шаблонов), поэтому повторные вызовы с тем же шаблоном дёшевы. Для шаблона в горячем цикле создайте один экземпляр Regex и держите его в поле static readonly; добавьте RegexOptions.Compiled, если он выполняется тысячи раз, обменяв более медленный старт на более быстрое сопоставление.

Тайм-ауты и катастрофический возврат

Некоторые шаблоны на определённых входных данных работают экспоненциальное время. Классический пример это вложенный квантификатор вроде ^(a+)+$ на строке "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!": движок перебирает все способы разделить a между двумя +, прежде чем сдаться. На веб-сервере один такой ввод от пользователя может занять ядро процессора (атака «ReDoS»). Задавайте тайм-аут любому регулярному выражению, которое обрабатывает недоверенный ввод:

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

У каждого статического метода тоже есть перегрузка с тайм-аутом. .NET 7 также добавил RegexOptions.NonBacktracking, движок с гарантированным линейным временем ценой некоторых возможностей (просмотров, обратных ссылок).

GeneratedRegex (.NET 7)

.NET 7 добавил генератор исходного кода, который превращает шаблон в обычный код C# при компиляции. Вы получаете скорость Compiled без затрат на запуск, сгенерированный код можно читать в IDE, и он работает с обрезкой и 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

В .NET 7 и новее это рекомендуемая форма для любого шаблона, известного при компиляции.

Частые ошибки

  • Нет якорей при проверке. Без ^...$ метод IsMatch принимает ввод, который лишь содержит совпадение.
  • Обычные строки для шаблонов. "\b" это возврат на шаг, а не граница слова. Используйте @"...".
  • Неэкранированные специальные символы. . совпадает с любым символом; шаблон 3.50 совпадает и с 3x50. Экранируйте через \. или через Regex.Escape для пользовательского ввода.
  • Жадные квантификаторы. <.*> на <b>bold</b> совпадает со всей строкой. Используйте ленивый .*? или отрицательный класс [^>]*.
  • Нет тайм-аута для недоверенного ввода. Вложенный квантификатор может подвесить запрос.
  • Регулярные выражения для структурированных форматов. HTML, JSON и CSV с кавычками требуют парсера, а не шаблона.

Часто задаваемые вопросы

Как использовать регулярные выражения в C#?

Добавьте using System.Text.RegularExpressions; и вызывайте статические методы Regex: Regex.IsMatch(input, pattern) возвращает bool, Regex.Match возвращает первое совпадение, Regex.Matches все совпадения, Regex.Replace выполняет замену, а Regex.Split разбивает. Записывайте шаблоны как verbatim-строки, @"\d+", чтобы обратные слеши доходили до движка регулярных выражений без изменений.

Как получить значение группы из совпадения regex в C#?

Заключите нужную часть в скобки и читайте match.Groups[1].Value (группы нумеруются с 1; группа 0 это всё совпадение). Именованные группы понятнее: (?<year>\d{4}) читается через match.Groups["year"].Value. Сначала проверьте match.Success, потому что у неудачного совпадения группы пустые, а не null.

Как заменить текст с помощью regex в C#?

Regex.Replace(input, pattern, replacement) заменяет каждое совпадение. Строка замены может ссылаться на группы: $1 для нумерованной группы, ${name} для именованной, $0 для всего совпадения. Для логики, которую шаблон выразить не может, передайте лямбду: Regex.Replace(text, @"\d+", m => (int.Parse(m.Value) * 2).ToString()).

Как сделать regex в C# нечувствительным к регистру?

Передайте RegexOptions.IgnoreCase: Regex.IsMatch(input, "error", RegexOptions.IgnoreCase). Параметры объединяются через |, например RegexOptions.IgnoreCase | RegexOptions.Multiline. Включить его можно и внутри шаблона через (?i).

Как проверить адрес email регулярным выражением в C#?

Шаблон вроде ^[^@\s]+@[^@\s]+\.[^@\s]+$ ловит очевидные опечатки (нет @, пробелы, нет точки в домене), и обычно форме больше ничего не нужно. Регулярное выражение не может по-настоящему проверить адрес: полная грамматика допускает формы, с которыми не справится ни один практичный шаблон, а синтаксически правильный адрес может не существовать. Проверьте базовую форму, а затем подтвердите адрес письмом.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ