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

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

  • поиск определенных шаблонов символов;
  • проверка текста на соответствие предопределенному шаблону (например, адресу электронной почты);
  • извлечение, изменение, замена или удаление текстовых подстрок;
  • добавление извлеченных строк в коллекцию для создания отчета.

Для многих приложений, которые работают со строками или анализируют большие блоки текста, регулярные выражения — незаменимый инструмент.

Принцип работы регулярных выражений

Главный компонент обработки текста с помощью регулярных выражений — это механизм регулярных выражений, представленный в .NET объектом System.Text.RegularExpressions.Regex. Как минимум, для обработки текста с использованием в регулярных выражений механизму регулярных выражений необходимо предоставить два следующих элемента:

  • Шаблон регулярного выражения для определения текста.

    В .NET шаблоны регулярных выражений определяются специальным синтаксисом или языком, который совместим с регулярными выражениями Perl 5 и добавляет дополнительные возможности, например сопоставление справа налево. Дополнительные сведения см. в разделе Элементы языка регулярных выражений. Краткий справочник.

  • Текст, который будет проанализирован на соответствие шаблону регулярного выражения.

Методы класса Regex позволяют выполнять следующие операции:

Обзор объектной модели регулярных выражений см. в разделе Объектная модель регулярных выражений.

Дополнительные сведения о языке регулярного выражения см. в разделе "Язык регулярных выражений" — краткий справочник или скачивание и печать одного из следующих брошюр:

Примеры регулярных выражений

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

Предупреждение

При использовании System.Text.RegularExpressions для обработки ненадежных входных данных передайте время ожидания. Злоумышленник может предоставить входные данные RegularExpressions, вызывая атаку типа "отказ в обслуживании". API платформы ASP.NET Core, использующие RegularExpressions, передают время ожидания.

Совет

Пространство имен System.Web.RegularExpressions содержит несколько объектов регулярных выражений, которые реализуют предопределенные шаблоны регулярных выражений для анализа строк из документов HTML, XML и ASP.NET. Например, TagRegex класс определяет теги запуска в строке, а CommentRegex класс определяет ASP.NET примечания в строке.

Пример 1. Замена подстроек

Предположим, что список рассылки содержит имена, в которые иногда входит обращение (Mr., Mrs., Miss или Ms.) в дополнение к имени и фамилии. Предположим, вы не хотите включать названия при создании меток конверта из списка. В этом случае можно использовать регулярное выражение для удаления заголовков, как показано в следующем примере:

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = "(Mr\\.? |Mrs\\.? |Miss |Ms\\.? )";
      string[] names = { "Mr. Henry Hunt", "Ms. Sara Samuels",
                         "Abraham Adams", "Ms. Nicole Norris" };
      foreach (string name in names)
         Console.WriteLine(Regex.Replace(name, pattern, String.Empty));
   }
}
// The example displays the following output:
//    Henry Hunt
//    Sara Samuels
//    Abraham Adams
//    Nicole Norris
Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Dim pattern As String = "(Mr\.? |Mrs\.? |Miss |Ms\.? )"
        Dim names() As String = {"Mr. Henry Hunt", "Ms. Sara Samuels", _
                                  "Abraham Adams", "Ms. Nicole Norris"}
        For Each name As String In names
            Console.WriteLine(Regex.Replace(name, pattern, String.Empty))
        Next
    End Sub
End Module
' The example displays the following output:
'    Henry Hunt
'    Sara Samuels
'    Abraham Adams
'    Nicole Norris

Шаблон (Mr\.? |Mrs\.? |Miss |Ms\.? ) регулярного выражения соответствует любому вхождениям "г-н", "г-жа", "Миссис", "Мисс", "г-жа", или "г-жа". После вызова метода Regex.Replace сопоставленная строка заменяется на String.Empty; другими словами, она удаляется из исходной строки.

Пример 2. Определение повторяющихся слов

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

using System;
using System.Text.RegularExpressions;

public class Class1
{
   public static void Main()
   {
      string pattern = @"\b(\w+?)\s\1\b";
      string input = "This this is a nice day. What about this? This tastes good. I saw a a dog.";
      foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
         Console.WriteLine("{0} (duplicates '{1}') at position {2}",
                           match.Value, match.Groups[1].Value, match.Index);
   }
}
// The example displays the following output:
//       This this (duplicates 'This') at position 0
//       a a (duplicates 'a') at position 66
Imports System.Text.RegularExpressions

Module modMain
    Public Sub Main()
        Dim pattern As String = "\b(\w+?)\s\1\b"
        Dim input As String = "This this is a nice day. What about this? This tastes good. I saw a a dog."
        For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
            Console.WriteLine("{0} (duplicates '{1}') at position {2}", _
                              match.Value, match.Groups(1).Value, match.Index)
        Next
    End Sub
End Module
' The example displays the following output:
'       This this (duplicates 'This') at position 0
'       a a (duplicates 'a') at position 66

Шаблон регулярного выражения \b(\w+?)\s\1\b интерпретируется следующим образом:

Расписание Интерпретация
\b Начало на границе слова.
(\w+?) Соответствует одному или нескольким символам слова (как можно меньшему количеству). Вместе они формируют группу, к которой можно обращаться как к \1.
\s Соответствует пробелу.
\1 Соответствует подстроке, равной группе с именем \1.
\b Соответствует границе слова.

Метод Regex.Matches вызывается с параметрами регулярного выражения RegexOptions.IgnoreCase. Поэтому операция сопоставления учитывает регистр, а пример указывает, что подстрока "This this" является повтором.

Входная строка содержит подстроку "this? This". Тем не менее, из-за пересекающегося знака препинания он не определяется как дублирование.

Пример 3. Динамическое создание регулярного выражения с учетом языка и региональных параметров

Следующий пример демонстрирует преимущества использования регулярных выражений с гибкими возможностями глобализации .NET. В примере объект NumberFormatInfo применяется для определения формата денежных значений в текущих региональных стандартах системы. Затем эти данные используются для динамического создания регулярного выражения, которое извлекает денежные значения из текста. Для каждого совпадения извлекается подгруппа, содержащая только числовые строки, которая преобразуется в значение Decimal, после чего рассчитывается промежуточный итог.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      // Define text to be parsed.
      string input = "Office expenses on 2/13/2008:\n" +
                     "Paper (500 sheets)                      $3.95\n" +
                     "Pencils (box of 10)                     $1.00\n" +
                     "Pens (box of 10)                        $4.49\n" +
                     "Erasers                                 $2.19\n" +
                     "Ink jet printer                        $69.95\n\n" +
                     "Total Expenses                        $ 81.58\n";

      // Get current culture's NumberFormatInfo object.
      NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
      // Assign needed property values to variables.
      string currencySymbol = nfi.CurrencySymbol;
      bool symbolPrecedesIfPositive = nfi.CurrencyPositivePattern % 2 == 0;
      string groupSeparator = nfi.CurrencyGroupSeparator;
      string decimalSeparator = nfi.CurrencyDecimalSeparator;

      // Form regular expression pattern.
      string pattern = Regex.Escape( symbolPrecedesIfPositive ? currencySymbol : "") +
                       @"\s*[-+]?" + "([0-9]{0,3}(" + groupSeparator + "[0-9]{3})*(" +
                       Regex.Escape(decimalSeparator) + "[0-9]+)?)" +
                       (! symbolPrecedesIfPositive ? currencySymbol : "");
      Console.WriteLine( "The regular expression pattern is:");
      Console.WriteLine("   " + pattern);

      // Get text that matches regular expression pattern.
      MatchCollection matches = Regex.Matches(input, pattern,
                                              RegexOptions.IgnorePatternWhitespace);
      Console.WriteLine("Found {0} matches.", matches.Count);

      // Get numeric string, convert it to a value, and add it to List object.
      List<decimal> expenses = new List<Decimal>();

      foreach (Match match in matches)
         expenses.Add(Decimal.Parse(match.Groups[1].Value));

      // Determine whether total is present and if present, whether it is correct.
      decimal total = 0;
      foreach (decimal value in expenses)
         total += value;

      if (total / 2 == expenses[expenses.Count - 1])
         Console.WriteLine("The expenses total {0:C2}.", expenses[expenses.Count - 1]);
      else
         Console.WriteLine("The expenses total {0:C2}.", total);
   }
}
// The example displays the following output:
//       The regular expression pattern is:
//          \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?)
//       Found 6 matches.
//       The expenses total $81.58.
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Text.RegularExpressions

Public Module Example
    Public Sub Main()
        ' Define text to be parsed.
        Dim input As String = "Office expenses on 2/13/2008:" + vbCrLf + _
                              "Paper (500 sheets)                      $3.95" + vbCrLf + _
                              "Pencils (box of 10)                     $1.00" + vbCrLf + _
                              "Pens (box of 10)                        $4.49" + vbCrLf + _
                              "Erasers                                 $2.19" + vbCrLf + _
                              "Ink jet printer                        $69.95" + vbCrLf + vbCrLf + _
                              "Total Expenses                        $ 81.58" + vbCrLf
        ' Get current culture's NumberFormatInfo object.
        Dim nfi As NumberFormatInfo = CultureInfo.CurrentCulture.NumberFormat
        ' Assign needed property values to variables.
        Dim currencySymbol As String = nfi.CurrencySymbol
        Dim symbolPrecedesIfPositive As Boolean = CBool(nfi.CurrencyPositivePattern Mod 2 = 0)
        Dim groupSeparator As String = nfi.CurrencyGroupSeparator
        Dim decimalSeparator As String = nfi.CurrencyDecimalSeparator

        ' Form regular expression pattern.
        Dim pattern As String = Regex.Escape(CStr(IIf(symbolPrecedesIfPositive, currencySymbol, ""))) + _
                                "\s*[-+]?" + "([0-9]{0,3}(" + groupSeparator + "[0-9]{3})*(" + _
                                Regex.Escape(decimalSeparator) + "[0-9]+)?)" + _
                                CStr(IIf(Not symbolPrecedesIfPositive, currencySymbol, ""))
        Console.WriteLine("The regular expression pattern is: ")
        Console.WriteLine("   " + pattern)

        ' Get text that matches regular expression pattern.
        Dim matches As MatchCollection = Regex.Matches(input, pattern, RegexOptions.IgnorePatternWhitespace)
        Console.WriteLine("Found {0} matches. ", matches.Count)

        ' Get numeric string, convert it to a value, and add it to List object.
        Dim expenses As New List(Of Decimal)

        For Each match As Match In matches
            expenses.Add(Decimal.Parse(match.Groups.Item(1).Value))
        Next

        ' Determine whether total is present and if present, whether it is correct.
        Dim total As Decimal
        For Each value As Decimal In expenses
            total += value
        Next

        If total / 2 = expenses(expenses.Count - 1) Then
            Console.WriteLine("The expenses total {0:C2}.", expenses(expenses.Count - 1))
        Else
            Console.WriteLine("The expenses total {0:C2}.", total)
        End If
    End Sub
End Module
' The example displays the following output:
'       The regular expression pattern is:
'          \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?)
'       Found 6 matches.
'       The expenses total $81.58.

На компьютере с региональными параметрами "English - United States (en-US)" пример динамически создает регулярное выражение \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?). Шаблон регулярного выражения интерпретируется следующим образом:

Расписание Интерпретация
\$ Выполняется поиск одного вхождения символа доллара ($) во входной строке. Строка шаблона регулярного выражения содержит обратную косую черту, что говорит о том, что символ доллара интерпретируется буквально, а не как привязка регулярного выражения. Символ $ только указывает, что обработчик регулярных выражений должен попытаться начать его совпадение в конце строки. Чтобы убедиться, что символ валюты текущего языка и региональных параметров не интерпретируется как символ регулярного выражения, в примере вызывается Regex.Escape метод для экранирования символа.
\s* Поиск нуля или нескольких вхождений пробела.
[-+]? Найдите ноль или одно вхождение положительного или отрицательного знака.
([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?) Внешние скобки определяют это выражение как записывающую группу или подэкспрессию. Если найдено соответствие, сведения об этой части строки можно получить из второго объекта Group в объекте GroupCollection, который возвращается свойством Match.Groups. Первый элемент в коллекции представляет все совпадение.
[0-9]{0,3} Поиск 0-3 вхождений десятичных цифр (0-9).
(,[0-9]{3})* Поиск нуля или нескольких вхождений разделителя группы, за которыми следуют три десятичные цифры.
\. Поиск одного вхождения десятичного разделителя.
[0-9]+ Поиск одной или нескольких десятичных цифр.
(\.[0-9]+)? Поиск нуля или одного вхождения десятичного разделителя, за которым следует по крайней мере одна десятичная цифра.

Если каждый подпаттерн найден в входной строке, совпадение завершается успешно, а Match объект, содержащий сведения о совпадении, добавляется в MatchCollection объект.

Заголовок Description
Элементы языка регулярных выражений — краткий справочник Сведения о наборе символов, операторов и конструкций, которые можно использовать для определения регулярных выражений.
Объектная модель регулярных выражений Сведения об использовании классов регулярных выражений и примеры кода.
Подробные сведения о поведении регулярных выражений Сведения о возможностях и поведении регулярных выражений платформы .NET.
Использование регулярных выражений в Visual Studio

Справочные материалы