Конструкции обратных ссылок

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

ПримечаниеПримечание

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

Платформа .NET Framework определяет отдельные элементы языка для ссылки на нумерованные и именованные группы захвата. Дополнительные сведения о захватываемых группах см. в разделе Конструкции группирования.

Нумерованные обратные ссылки

Нумерованная обратная ссылка использует следующий синтаксис:

\number

где номер — это порядковое положение группы записи, определенное в шаблоне регулярного выражения. Например, \4 соответствует содержимому четвертой захватываемой группы. Если номер не определен в шаблоне регулярного выражения, то возникает ошибка синтаксического анализа, и обработчик регулярных выражений создает исключение ArgumentException. Например, регулярное выражение \b(\w+)\s\1 является допустимым, поскольку (\w+) является первой и единственной захватываемой группой в выражении. С другой стороны, выражение \b(\w+)\s\2 недопустимо и создает исключение аргумента, поскольку нет группы захвата с номером \2.

Следует отметить неоднозначность такой записи, используемой как в восьмеричных escape-кодах (\16), так и в обратных ссылках \число. Эта неопределенность разрешается следующим образом:

  • Выражения \1 в \9 всегда являются обратными ссылками, а не восьмеричными числами.

  • Если первая цифра многоразрядного выражения – 8 или 9 (например \80 или \91), то выражение интерпретируется как литерал.

  • Выражения от \10 и более считаются обратными ссылками, если имеется обратная ссылка, соответствующая этому номеру; в противном случае они интерпретируются как восьмеричные коды.

  • Если регулярное выражение содержит обратную ссылку на неопределенный номер группы, возникает ошибка синтаксического анализа, и обработчик регулярных выражений создает исключение ArgumentException.

Если неоднозначность представляет проблему, можно использовать представление \k<имя>, которое является однозначным, и его невозможно спутать Нельзя путать с восьмеричными кодами знаков. Аналогичным образом шестнадцатеричные коды, например \xdd, однозначны и их нельзя спутать с обратными ссылками.

В следующем примере в строке выделяются символы, представленные двойными словами. Определяет регулярное выражение, (\w)\1, которое состоит из следующих элементов.

Элемент

Описание

(\w)

Выделяет буквенный символ и назначает его первой группе записи.

\1

Выделить следующий символ, значение которого совпадает с первой группой записи.

Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "(\w)\1"
      Dim input As String = "trellis llama webbing dresser swagger"
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Found '{0}' at position {1}.", _
                           match.Value, match.Index)
      Next   
   End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(\w)\1";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.", 
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.

Именованные обратные ссылки

Именованная обратная ссылка задается с помощью следующего синтаксиса:

\k<name>

или

\k'name'

где имя — это имя группы записи, определенное в шаблоне регулярного выражения. Если имя не определено в шаблоне регулярного выражения, то возникает ошибка синтаксического анализа, и обработчик регулярных выражений создает исключение ArgumentException.

В следующем примере в строке выделяются символы, представленные двойными словами. Определяет регулярное выражение, (?<char>\w)\k<char>, которое состоит из следующих элементов.

Элемент

Описание

(?<char>\w)

Выделяет буквенный символ и назначает его группе записи с именем char.

\k<char>

Выделить следующий символ, значение которого совпадает с группой записи char.

Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "(?<char>\w)\k<char>"
      Dim input As String = "trellis llama webbing dresser swagger"
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Found '{0}' at position {1}.", _
                           match.Value, match.Index)
      Next   
   End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<char>\w)\k<char>";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.", 
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.

Обратите внимание, что имя также может быть строковым представлением числа. Например, в следующем примере используется регулярное выражение (?<2>\w)\k<2> для поиска в строке двойных буквенных символов.

Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "(?<2>\w)\k<2>"
      Dim input As String = "trellis llama webbing dresser swagger"
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Found '{0}' at position {1}.", _
                           match.Value, match.Index)
      Next   
   End Sub
End Module
' The example displays the following output:
'       Found 'll' at position 3.
'       Found 'll' at position 8.
'       Found 'bb' at position 16.
'       Found 'ss' at position 25.
'       Found 'gg' at position 33.
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<2>\w)\k<2>";
      string input = "trellis llama webbing dresser swagger";
      foreach (Match match in Regex.Matches(input, pattern))
         Console.WriteLine("Found '{0}' at position {1}.", 
                           match.Value, match.Index);
   }
}
// The example displays the following output:
//       Found 'll' at position 3.
//       Found 'll' at position 8.
//       Found 'bb' at position 16.
//       Found 'ss' at position 25.
//       Found 'gg' at position 33.

С чем сопоставляются обратные ссылки

Обратная ссылка относится к самому недавнему определению группы (самому ближнему слева определению при обработке слева направо). Если из группы создается несколько шаблонов для поиска, то обратная ссылка относится к самому последнему шаблону.

Следующий пример включает шаблон регулярного выражения (?<1>a)(?<1>\1b)*, который переопределяет именованную группу \1. В следующей таблице описывается каждый шаблон регулярного выражения.

Шаблон

Описание

(?<1>a)

Выделить символ "a" и присвоить результат группе записи с именем 1.

(?<1>\1b)*

Выделяет 0 или 1 вхождение группы с именем 1 вместе с "b" и присваивает результат группе записи с именем 1.

Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "(?<1>a)(?<1>\1b)*"
      Dim input As String = "aababb"
      For Each match As Match In Regex.Matches(input, pattern)
         Console.WriteLine("Match: " + match.Value)
         For Each group As Group In match.Groups
            Console.WriteLIne("   Group: " + group.Value)
         Next
      Next
   End Sub
End Module
' The example display the following output:
'          Group: aababb
'          Group: abb
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"(?<1>a)(?<1>\1b)*";
      string input = "aababb";
      foreach (Match match in Regex.Matches(input, pattern))
      {
         Console.WriteLine("Match: " + match.Value);
         foreach (Group group in match.Groups)
            Console.WriteLine("   Group: " + group.Value);
      }
   }
}
// The example displays the following output:
//          Group: aababb
//          Group: abb

При сравнении регулярного выражения со строкой ввода ("aababb") обработчик регулярных выражений выполняет следующие операции:

  1. Начинается с первого символа строки и сопоставляет "a" с выражением (?<1>a). Теперь значение группы 1 будет равно "a".

  2. Перемещается ко второму символу и успешно сопоставляет строку "ab" с выражением \1b или "ab". Затем результат, "ab", присваивается свойству \1.

  3. Перемещает к четвертому символу. Выражение (?<1>\1b) должно выдать ноль или больше соответствий, поэтому он успешно сопоставляет строку "abb" с выражением \1b. Результат, "abb", передается в \1.

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

Если для группы не было найдено ни одной подстроки, то обратная ссылка на эту группу не определена и не работает. Это иллюстрируется шаблоном регулярного выражения \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b, который определяется следующим образом:

Шаблон

Описание

\b

Совпадение должно начинаться на границе слова.

(\p{Lu}{2})

Выделить две прописные буквы. Это первая группа записи.

(\d{2})?

Выделить ноль или одно вхождение двух десятичных цифр. Это вторая группа записи.

(\p{Lu}{2})

Выделить две прописные буквы. Это третья группа записи.

\b

Совпадение должно заканчиваться на границе слова.

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

Imports System.Text.RegularExpressions

Module Example
   Public Sub Main()
      Dim pattern As String = "\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b"
      Dim inputs() As String = { "AA22ZZ", "AABB" }
      For Each input As String In inputs
         Dim match As Match = Regex.Match(input, pattern)
         If match.Success Then
            Console.WriteLine("Match in {0}: {1}", input, match.Value)
            If match.Groups.Count > 1 Then
               For ctr As Integer = 1 To match.Groups.Count - 1
                  If match.Groups(ctr).Success Then
                     Console.WriteLine("Group {0}: {1}", _
                                       ctr, match.Groups(ctr).Value)
                  Else
                     Console.WriteLine("Group {0}: <no match>", ctr)
                  End If      
               Next
            End If
         End If
         Console.WriteLine()
      Next      
   End Sub
End Module
' The example displays the following output:
'       Match in AA22ZZ: AA22ZZ
'       Group 1: AA
'       Group 2: 22
'       Group 3: ZZ
'       
'       Match in AABB: AABB
'       Group 1: AA
'       Group 2: <no match>
'       Group 3: BB
using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      string pattern = @"\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b";
      string[] inputs = { "AA22ZZ", "AABB" };
      foreach (string input in inputs)
      {
         Match match = Regex.Match(input, pattern);
         if (match.Success)
         {
            Console.WriteLine("Match in {0}: {1}", input, match.Value);
            if (match.Groups.Count > 1)
            {
               for (int ctr = 1; ctr <= match.Groups.Count - 1; ctr++)
               {
                  if (match.Groups[ctr].Success)
                     Console.WriteLine("Group {0}: {1}", 
                                       ctr, match.Groups[ctr].Value);
                  else
                     Console.WriteLine("Group {0}: <no match>", ctr);
               }
            }
         }
         Console.WriteLine();
      }      
   }
}
// The example displays the following output:
//       Match in AA22ZZ: AA22ZZ
//       Group 1: AA
//       Group 2: 22
//       Group 3: ZZ
//       
//       Match in AABB: AABB
//       Group 1: AA
//       Group 2: <no match>
//       Group 3: BB

См. также

Основные понятия

Элементы языка регулярных выражений