Українська
Заміна, параметри та перегляд
Заміна та розбиття тексту
Метод Replace
Метод Replace повертає новий рядок, у якому кожен збіг замінено рядком заміни. У рядку заміни можна посилатися на групи (табл. 2.4, https://learn.microsoft.com/dotnet/standard/base-types/substitutions-in-regular-expressions).
Таблиця 2.4. Підстановки в рядку заміни
| Підстановка | Що вставляється |
|---|---|
$1, $2 | текст нумерованої групи |
${name} | текст іменованої групи |
${1}0 | група 1, потім символ 0 (запис $10 означав би групу 10) |
$0 | увесь збіг |
$$ | символ $ |
cs
Regex.Replace(" Багато зайвих пробілів ", @"\s+", " ").Trim();
// "Багато зайвих пробілів"
Regex.Replace("17.09.2026 і 01.10.2026",
@"(\d{2})\.(\d{2})\.(\d{4})", "$3-$2-$1");
// "2026-09-17 і 2026-10-01"
Regex.Replace("Ціна 250", @"\d+", "$0 грн"); // "Ціна 250 грн"Такі самі заміни виконує вікно Replace in Files у Visual Studio: пошук і заміна в редакторі використовують регулярні вирази .NET (https://learn.microsoft.com/visualstudio/ide/using-regular-expressions-in-visual-studio). Досить увімкнути кнопку Use Regular Expressions (Alt+E) (рис. 2.6).

Рис. 2.6. Пошук і заміна з регулярними виразами у Visual Studio
Заміна з MatchEvaluator
Якщо рядок заміни залежить від обробки збігу (обчислення, перевірки, форматування), замість рядка передають метод або лямбда-вираз типу MatchEvaluator: він отримує Match і повертає рядок заміни (https://learn.microsoft.com/dotnet/api/system.text.regularexpressions.matchevaluator):
cs
string masked = Regex.Replace("Олена Петренко, Ігор Коваль",
@"\b(\p{Lu})\p{Ll}+\b", m => m.Groups[1].Value + ".");
// "О. П., І. К."
string doubled = Regex.Replace("ціна 12,5 і 7,25", @"\d+,\d+",
m => (decimal.Parse(m.Value) * 2).ToString());
// "ціна 25,0 і 14,50"Метод Split і Regex.Escape
Regex.Split ділить рядок у місцях збігів. На відміну від string.Split, роздільник може бути будь-яким шаблоном, наприклад «кома, крапка з комою або риска разом із пробілами навколо». Якщо шаблон містить групу, захоплені роздільники також потрапляють у результат:
cs
Regex.Split("яблука, груші;сливи | вишні", @"\s*[,;|]\s*");
// яблука / груші / сливи / вишні
Regex.Split("2+3*4-1", @"([+*-])");
// 2 / + / 3 / * / 4 / - / 1Коли частину шаблону вводить користувач (наприклад, рядок для пошуку), її обов’язково пропускають через Regex.Escape. Метод екранує метасимволи й пробіли, тому текст шукається буквально. Без екранування рядок C++ спричиняє виняток RegexParseException (Nested quantifier '+'):
cs
string find = "C++";
Regex.Escape("ціна (грн): 12.50?"); // ціна\ \(грн\):\ 12\.50\?
Regex.Count("C++ і C++/CLI, але не C", Regex.Escape(find)); // 2Приклад 3. Нормалізація дат
Програма перетворює дати з українського формату дд.мм.рррр на міжнародний формат ISO 8601 рррр-мм-дд. Спочатку виконується проста підстановка груп, а потім – заміна з MatchEvaluator, яка перевіряє, чи існує така дата в календарі.
cs
using System.Globalization;
using System.Text.RegularExpressions;
Console.OutputEncoding = System.Text.Encoding.UTF8;
string text = """
Договір № 17/2026 від 01.09.2026 діє до 31.12.2026.
Акт від 31.02.2026 підписано 3.10.2026, версія 2.14.2026.1.
""";
var date = new Regex(
@"\b(?<day>\d{1,2})\.(?<month>\d{1,2})\.(?<year>\d{4})\b");
// 1. Підстановка груп: лише перестановка частин.
Console.WriteLine(date.Replace(text, "${year}-${month}-${day}"));
// 2. MatchEvaluator: перевірка дати та доповнення нулями.
int fixedCount = 0, invalid = 0;
string result = date.Replace(text, match =>
{
string value = match.Value;
if (DateOnly.TryParseExact(value, "d.M.yyyy",
CultureInfo.InvariantCulture, DateTimeStyles.None,
out DateOnly parsed))
{
fixedCount++;
return parsed.ToString("yyyy-MM-dd");
}
invalid++;
return $"[{value}?]";
});
Console.WriteLine();
Console.WriteLine(result);
Console.WriteLine($"Дат: {fixedCount}, помилкових: {invalid}");Регулярний вираз перевіряє лише форму тексту: він не знає, що в лютому немає 31 дня і що 2.14.2026.1 – номер версії, а не дата. Тому перша заміна створює неіснуючі дати 2026-02-31, 2026-14-2 і не доповнює день нулем. Друга заміна перевіряє кожен збіг методом DateOnly.TryParseExact, форматує правильні дати та позначає помилкові:
Договір № 17/2026 від 2026-09-01 діє до 2026-12-31.
Акт від 2026-02-31 підписано 2026-10-3, версія 2026-14-2.1.
Договір № 17/2026 від 2026-09-01 діє до 2026-12-31.
Акт від [31.02.2026?] підписано 2026-10-03, версія [2.14.2026?].1.
Дат: 3, помилкових: 2Параметри RegexOptions
Поведінку рушія змінюють параметри перелічення RegexOptions, які передаються в конструктор або статичний метод і поєднуються операцією | (табл. 2.5). Частину параметрів можна задати вбудованою опцією прямо в шаблоні: (?i) на початку або (?i:…) для частини шаблону (https://learn.microsoft.com/dotnet/standard/base-types/regular-expression-options).
Таблиця 2.5. Параметри RegexOptions
| Параметр | У шаблоні | Дія |
|---|---|---|
IgnoreCase | (?i) | не розрізняти великі й малі літери |
Multiline | (?m) | ^ і $ – початок і кінець кожного рядка тексту |
Singleline | (?s) | . відповідає також \n |
IgnorePatternWhitespace | (?x) | ігнорувати пробіли в шаблоні, коментарі після # |
ExplicitCapture | (?n) | захоплювати лише іменовані групи |
CultureInvariant | – | порівнювати регістр незалежно від культури |
Compiled | – | компілювати шаблон у IL-код під час виконання |
NonBacktracking | – | рушій без повернень із лінійним часом (.NET 7+) |
ECMAScript | – | поведінка JavaScript: \d і \w лише ASCII |
RightToLeft | – | пошук справа наліво |
cs
string text = "1. Вступ\n2. Синтаксис\nтекст\n3. Класи";
Regex.Count(text, @"^\d+\."); // 1
Regex.Count(text, @"^\d+\.", RegexOptions.Multiline); // 3
Regex.Count(text, @"(?m)^\d+\."); // 3
string comment = "/* рядок 1\nрядок 2 */";
Regex.IsMatch(comment, @"/\*.*\*/"); // False
Regex.IsMatch(comment, @"/\*.*\*/", RegexOptions.Singleline); // True
Regex.IsMatch("ЇЖАК", "(?i)їжак"); // True
Regex.IsMatch("Rome CITY", "Rome (?i:city)"); // TrueНазви параметрів Multiline і Singleline не протилежні, як здається: перший змінює роботу ^/$, другий – крапки, і їх можна вмикати разом. IgnorePatternWhitespace дозволяє розбити складний шаблон на рядки з коментарями після #, як у прикладі 2.
Увага
У .NET 10 символ $ у режимі Multiline відповідає лише позиції перед \n. Для тексту з кінцями рядків Windows \r\n шаблон ^.+$ захоплює й символ \r, а ^\w+$ не знаходить жодного рядка. Пишіть \r?$ або заздалегідь замінюйте \r\n на \n. Параметр RegexOptions.AnyNewLine, що розпізнає всі види кінців рядків, з’являється лише в .NET 11.
Параметр CultureInvariant потрібен разом з IgnoreCase, якщо шаблон порівнює службові слова, а програма може працювати з будь-якою культурою. У турецькій культурі мала «i» відповідає великій «İ» з крапкою, тому з IgnoreCase рядок FILE не відповідає шаблону file, а з IgnoreCase | CultureInvariant – відповідає.
Перегляд уперед і назад
Перегляд (lookaround) – це умова, яка перевіряє текст праворуч або ліворуч від поточної позиції, не включаючи його в збіг. Як і якорі, перегляд має нульову ширину (табл. 2.6).
Таблиця 2.6. Перегляд уперед і назад
| Запис | Назва | Приклад |
|---|---|---|
X(?=Y) | перегляд уперед: далі йде Y | \d+(?=\s?грн) – число перед «грн» |
X(?!Y) | негативний перегляд уперед: далі немає Y | \b\d+\b(?!%) – число, не відсоток |
(?<=Y)X | перегляд назад: перед X стоїть Y | (?<=№\s?)\d+ – номер після «№» |
(?<!Y)X | негативний перегляд назад: перед X немає Y | (?<!\d)\d{4}(?!\d) – рівно 4 цифри |
cs
string price = "Ціна 250 грн, знижка 15 %, доставка 1250грн";
Regex.Matches(price, @"\d+(?=\s?грн)"); // 250, 1250
Regex.Matches("Рахунки №42 і № 107", @"(?<=№\s?)\d+"); // 42, 107
Regex.Matches("знижка 15%, 20 шт.", @"\d+(?!%)"); // 1, 20 (!)
Regex.Matches("знижка 15%, 20 шт.", @"\b\d+\b(?!%)"); // 20
Regex.Replace("1234567", @"(?<=\d)(?=(?:\d{3})+\b)", " ");
// "1 234 567"Третій рядок показує пастку: для 15% шаблон \d+(?!%) не знаходить 15, але рушій повертається на одну цифру назад і знаходить 1, після якої стоїть не %, а 5. Межі слова \b забороняють такі «обрізані» збіги. Останній приклад вставляє пробіл у позиції, перед якою є цифра, а після якої кількість цифр до кінця числа кратна трьом.
Кілька переглядів уперед на початку шаблону перевіряють кілька незалежних умов для одного рядка. Так записують політику паролів: щонайменше 8 символів, серед них цифра, мала та велика літери:
cs
var password = new Regex(@"^(?=.*\d)(?=.*\p{Ll})(?=.*\p{Lu}).{8,}$");
password.IsMatch("Rome2026"); // True
password.IsMatch("rome2026"); // False – немає великої літериКожен перегляд (?=.*…) проходить рядок від початку і нічого не «витрачає», тому наступна умова знову перевіряється з позиції 0. Щоб повідомити користувачеві, яку саме умову порушено, кожне правило перевіряють окремим шаблоном.