Регулярные выражения

Регулярное выражение – это некий шаблон, составленный из символов и спецсимволов, который позволяет находить подстроки соответствующие этому шаблону в других строках.

Классы, предназначенные для работы с регулярными выражениями, содержатся в сборке System.dll и описаны в пространстве имен System.Text.RegularExpressions. Основным классом, обеспечивающим реализацию всех действий, связанных с использованием регулярных выражений (поиск, замена, разбиение строки), является класс Regex. Все прочие классы являются вспомогательными, и используются при описании параметров методов класса Regex или их возвращаемых значений.

Класс Regex имеет конструктор, который принимает регулярное выражение:

Regex regex = new Regex(pattern);
Здесь pattern - строка, содержащая регулярное выражение. Описание класса со всеми вариантами конструктора.

Для обработки строк с помощью регулярных выражений не обязательно создавать экземпляры класса Regex; вместо этого можно использовать статические методы данного класса.

Методы класса Regex

Все основные методы класса Regex реализованы в двух вариантах: статическом и экземплярном; при этом каждый из вариантов реализован для нескольких наборов параметров. Имеется шесть основных методов:
  • IsMatch (типа bool) - возвращает True, если требуемое выражение найдено, и False в противном случае; 
  • Match (типа Match) - возвращает первое найденное выражение; 
  • Matches (типа MatchCollection) - возвращает все найденные выражения; 
  • Split (типа string[]) - разбивает строку на фрагменты; разделители фрагментов определяются регулярным выражением; 
  • Replace (типа string) - заменяет найденные выражения.

Проверка на соответствие сроки формату

Задача: определить, является ли заданная строка ростовским городским телефонным номером в федеральном формате (формат номера: +7 (863) 2**-**-** или +7 (863) 3**-**-**). 

Такая задача может быть решена в виде следующей функции:

static bool IsPhoneNo(string input)
{
    Regex regex = new Regex(@"^\+7\s*\(863\)\s*[23]\d{2}-\d{2}-\d{2}$");
    return regex.IsMatch(input);
}
или (если использовать статический метод)
static bool IsPhoneNo(string input) => 
    Regex.IsMatch(input, @"^\+7\s*\(863\)\s*[23]\d{2}-\d{2}-\d{2}$");
Здесь перед началом строки регулярного выражения стоит символ «@» который указывает компилятору воспринимать все символы буквально. Это необходимо, чтобы корректно воспринимался символ «\».

Использование Match

Задача: найти в строке первое слово, которое начинается с буквы 'a'
static string FirstWordStartingWithA(string input)
{
    var match = Regex.Match(input, @"\ba\w*\b", RegexOptions.IgnoreCase);
    return match.Value;
}

Метод Match возвращает значение класса Match. Value - свойство класса, которое соответствует найденной подстроке. Прочие свойства класса Match.

Поиск в строке всех вхождений регулярного выражения

Задача: Дана строка, содержащая слова, знаки пунктуации и числа в десятичной системе счисления. Сформировать последовательность строк, содержащий все строковые представления чисел из исходной строки в том же порядке.
static IEnumerable<string> AllNumbers(string input) => 
    Regex.Matches(input, @"\b\d+\b").Select(x => x.Value);
Метод Match возвращает значение класса MatchCollection. Элементы MatchCollection можно перебрать в цикле
var s = "red green gray blue";
foreach (Match m in Regex.Matches(s,@"\w+"))
    WriteLine($"{m.Value} {m.Index}");

Разбиение строки на фрагменты

Задача: Дана строка, содержащая слова, разделённые одним или несколькими пробелами. Сформировать последовательность строк, содержащий все слова из исходной строки в том же порядке.
static string[] WordsInString(string input) 
    => Regex.Split(input, @"\s+");

Группирование и ссылки на группы

С помощью круглых скобок можно формировать группы регулярного выражения, на которые в дальнейшем можно ссылаться по номеру или по имени: 

  • (expr) - включить соответствие для выражения expr в нумерованную группу (группы нумеруются от 1 согласно порядку следования их открывающих скобок; группа 0 соответствует всему найденному вхождению);
  •  (?<name>expr) или (?'name'expr) - включить соответствие для выражения expr в именованную группу с именем name;
  •  (?:expr) - группирующее выражение, не связываемое с нумерованной или именованной группой; 
  • \N - ссылка на ранее найденную группу с номером N, означающая, что в данной позиции регулярного выражения должен содержаться текст этой группы; 
  • \k - ссылка на группу с именем nаme.

Задача: Написать функцию, которая по заданному телефонному номеру в федеральном формате (+7 (код_города) городской_номер) возвращает городской телефонный номер. Считать, что код города может состоять из трёх или четырёх цифр. Если исходная строка не соответствует заданному формату, возвращать пустую строку. 

static string CityNum(string input) => 
    Regex.Match(input, @"\+7\s*\(\d{3,4}\)\s*(\d{2,3}\-\d{2}\-\d{2})")
        .Groups[1].Value;

Замены при помощи регулярных выражений

В выражениях замены используются управляющие последовательности (подстановки), отличные от директив языка регулярных выражений. Ниже приводятся наиболее часто используемые подстановки:

  • $$ - символ $;
  • $0 - все найденное вхождение;
  • $_ - вся исходная строка;
  • $N - найденная группа с номером N (или пустая строка, если группа не найдена);
  • ${name} - найденная группа с именем name (или пустая строка, если группа не найдена).

Примеры:
var s = "Взять все слова в угловые скобки";
s = Regex.Replace(s,@"\b\w+\b", "<$0>");
Console.WriteLine(s);
В случае сложных видов замены используется вариант метода Replace с параметром-делегатом MatchEvaluator.
var s = "Удвоить все числа в строке: 10+12=22";
s = Regex.Replace(s, @"\d+", m => (int.Parse(m.Value) * 2).ToString());
Console.WriteLine(s);
var s = "Изменить формат даты: 3/18/2019";
s = Regex.Replace(s, @"(\d{1,2})/(\d{1,2})/(\d{1,4})", @"$2.$1.$3" );
Console.WriteLine(s);
Последнее изменение: Monday, 18 March 2019, 10:27