Konstrukcje dopasowań w wyrażeniach regularnych

Odwołania wsteczne zapewniają wygodny sposób identyfikowania powtarzającego się znaku lub podciągów w ciągu. Jeśli na przykład ciąg wejściowy zawiera wiele wystąpień dowolnego podciągu, możesz dopasować pierwsze wystąpienie do grupy przechwytywania, a następnie użyć wnioskowania wstecznego, aby dopasować kolejne wystąpienia podciągów.

Uwaga

Oddzielna składnia służy do odwoływania się do nazwanych i numerowanych grup przechwytywania w ciągach zastępczych. Aby uzyskać więcej informacji, zobacz Podstawianie.

Platforma .NET definiuje oddzielne elementy języka w celu odwoływania się do grup numerowanych i nazwanych. Aby uzyskać więcej informacji na temat przechwytywania grup, zobacz Grouping Constructs (Konstrukcje grupowania).

Numerowane odwołania wsteczne

Ponumerowana funkcja backreference używa następującej składni:

\Numer

gdzie liczba jest pozycją porządkową grupy przechwytywania w wyrażeniu regularnym. Na przykład \4 pasuje do zawartości czwartej grupy przechwytywania. Jeśli liczba nie jest zdefiniowana we wzorcu wyrażenia regularnego, wystąpi błąd analizowania, a aparat wyrażeń regularnych ArgumentExceptionzgłasza błąd . Na przykład wyrażenie \b(\w+)\s\1 regularne jest prawidłowe, ponieważ (\w+) jest pierwszym i tylko przechwytywaniem grupy w wyrażeniu. Z drugiej strony \b(\w+)\s\2 jest nieprawidłowy i zgłasza wyjątek argumentu, ponieważ nie ma przechwycenia grupy numerowanej \2. Ponadto, jeśli liczba identyfikuje grupę przechwytywania w określonej pozycji porządkowej, ale ta grupa przechwytywania została przypisana nazwa liczbowa inna niż pozycja porządkowa, analizator wyrażeń regularnych również zgłasza ArgumentExceptionwartość .

Zwróć uwagę na niejednoznaczność między kodami ucieczki ósemkowej (takimi jak \16) i \odwołaniami liczb , które używają tej samej notacji. Ta niejednoznaczność jest rozpoznawana w następujący sposób:

  • Wyrażenia \1 przez \9 są zawsze interpretowane jako backreferences, a nie jako kody ósemkowe.

  • Jeśli pierwsza cyfra wyrażenia wielodigitowego to 8 lub 9 (na przykład \80 lub \91), wyrażenie interpretowane jako literał.

  • Wyrażenia z \10 i większe są uznawane za backreferences, jeśli istnieje backreference odpowiadające tej liczbie; w przeciwnym razie są interpretowane jako kody ósemkowe.

  • Jeśli wyrażenie regularne zawiera odwołanie do niezdefiniowanego numeru grupy, wystąpi błąd analizy, a aparat wyrażeń regularnych ArgumentExceptionzgłasza błąd .

Jeśli niejednoznaczność jest problemem, możesz użyć \k<notacji nazw> , która jest jednoznaczna i nie może być mylić z kodami znaków ósemkowych. Podobnie kody szesnastkowe, takie jak \xdd są jednoznaczne i nie można ich mylić z backreferences.

Poniższy przykład znajduje podwójne znaki wyrazów w ciągu. Definiuje wyrażenie regularne , (\w)\1które składa się z następujących elementów.

Element opis
(\w) Dopasuj znak wyrazu i przypisz go do pierwszej grupy przechwytywania.
\1 Dopasuj następny znak, który jest taki sam jak wartość pierwszej grupy przechwytywania.
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.
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.

Nazwane odwołania wsteczne

Nazwane wnioskowanie wsteczne jest definiowane przy użyciu następującej składni:

\k<name>

or:

\k'name'

gdzie nazwa to nazwa grupy przechwytywania zdefiniowanej we wzorcu wyrażenia regularnego. Jeśli nazwa nie jest zdefiniowana we wzorcu wyrażenia regularnego, wystąpi błąd analizy, a aparat wyrażeń regularnych ArgumentExceptionzgłasza błąd .

Poniższy przykład znajduje podwójne znaki wyrazów w ciągu. Definiuje wyrażenie regularne , (?<char>\w)\k<char>które składa się z następujących elementów.

Element opis
(?<char>\w) Dopasuj znak wyrazu i przypisz go do grupy przechwytywania o nazwie char.
\k<char> Dopasuj następny znak, który jest taki sam jak wartość char grupy przechwytywania.
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.
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.

Nazwane backreferences liczbowe

W nazwanym backreference z \knazwą nazwa może być również ciągiem reprezentującym liczbę. Na przykład w poniższym przykładzie użyto wyrażenia (?<2>\w)\k<2> regularnego do znalezienia podwójnych znaków słów w ciągu. W tym przypadku przykład definiuje grupę przechwytywania, która jest jawnie nazwana "2", a wnioskowanie wsteczne ma odpowiednio nazwę "2".

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.
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.

Jeśli nazwa jest ciągiem reprezentującym liczbę, a żadna grupa przechwytywania nie ma tej nazwy, \k<nazwa> jest taka sama jak liczba wsteczna\, gdzie liczba jest pozycją porządkową przechwytywania. W poniższym przykładzie istnieje pojedyncza grupa przechwytywania o nazwie char. Konstrukcja wnioskowania wstecznego odwołuje się do niej jako \k<1>. Jak pokazano w danych wyjściowych z przykładu, wywołanie metody Regex.IsMatch zakończy się powodzeniem, ponieważ char jest pierwszą grupą przechwytywania.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Console.WriteLine(Regex.IsMatch("aa", @"(?<char>\w)\k<1>"));
      // Displays "True".
   }
}

Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Console.WriteLine(Regex.IsMatch("aa", "(?<char>\w)\k<1>"))
        ' Displays "True".
    End Sub
End Module

Jeśli jednak nazwa jest ciągiem reprezentującym liczbę i grupą przechwytywania w tej pozycji została jawnie przypisana nazwa liczbowa, analizator wyrażeń regularnych nie może zidentyfikować grupy przechwytywania według położenia porządkowego. Zamiast tego zgłasza wartość ArgumentException. Jedyna grupa przechwytywania w poniższym przykładzie nosi nazwę "2". \k Ponieważ konstrukcja służy do definiowania wnioskowania wstecznego o nazwie "1", analizator wyrażeń regularnych nie może zidentyfikować pierwszej grupy przechwytywania i zgłasza wyjątek.

using System;
using System.Text.RegularExpressions;

public class Example
{
   public static void Main()
   {
      Console.WriteLine(Regex.IsMatch("aa", @"(?<2>\w)\k<1>"));
      // Throws an ArgumentException.
   }
}

Imports System.Text.RegularExpressions

Module Example
    Public Sub Main()
        Console.WriteLine(Regex.IsMatch("aa", "(?<2>\w)\k<1>"))
        ' Throws an ArgumentException.
    End Sub
End Module

Jakie dopasowania backreferences

Odwołanie wsteczne odnosi się do najnowszej definicji grupy (definicja najbardziej natychmiast po lewej stronie, podczas dopasowywania od lewej do prawej). Gdy grupa wykonuje wiele przechwytywania, wnioskowanie wsteczne odnosi się do najnowszego przechwytywania.

Poniższy przykład zawiera wzorzec wyrażenia regularnego , (?<1>a)(?<1>\1b)*który ponownie definiuje grupę o nazwie \1. W poniższej tabeli opisano każdy wzorzec w wyrażeniu regularnym.

Wzorzec opis
(?<1>a) Dopasuj znak "a" i przypisz wynik do grupy przechwytywania o nazwie 1.
(?<1>\1b)* Dopasuj zero lub więcej wystąpień grupy o nazwie 1 wraz z "b" i przypisz wynik do grupy przechwytywania o nazwie 1.
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
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

Porównując wyrażenie regularne z ciągiem wejściowym ("aababb"), aparat wyrażeń regularnych wykonuje następujące operacje:

  1. Rozpoczyna się na początku ciągu i pomyślnie pasuje do "a" z wyrażeniem (?<1>a). Wartość 1 grupy to teraz "a".

  2. Przechodzi do drugiego znaku i pomyślnie pasuje do ciągu "ab" z wyrażeniem \1b, lub "ab". Następnie przypisuje wynik "ab" do \1.

  3. Przechodzi do czwartego znaku. Wyrażenie (?<1>\1b)* ma być dopasowane zero lub więcej razy, więc pomyślnie pasuje do ciągu "abb" z wyrażeniem \1b. Przypisuje wynik "abb", z powrotem do \1.

W tym przykładzie * jest kwantyfikatorem pętli — jest obliczany wielokrotnie, dopóki aparat wyrażeń regularnych nie będzie mógł dopasować zdefiniowanego wzorca. Kwantyfikatory pętli nie usuwają definicji grup.

Jeśli grupa nie przechwyciła żadnych podciągów, odwołanie wsteczne do tej grupy jest niezdefiniowane i nigdy nie jest zgodne. Jest to zilustrowane przez wzorzec \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\bwyrażenia regularnego , który jest zdefiniowany w następujący sposób:

Wzorzec opis
\b Rozpocznij dopasowanie na granicy wyrazu.
(\p{Lu}{2}) Dopasuj dwie wielkie litery. Jest to pierwsza grupa przechwytywania.
(\d{2})? Dopasuj zero lub jedno wystąpienie dwóch cyfr dziesiętnych. Jest to druga grupa przechwytywania.
(\p{Lu}{2}) Dopasuj dwie wielkie litery. Jest to trzecia grupa przechwytywania.
\b Zakończ dopasowanie na granicy wyrazu.

Ciąg wejściowy może być zgodny z tym wyrażeniem regularnym, nawet jeśli dwie cyfry dziesiętne zdefiniowane przez drugą grupę przechwytywania nie są obecne. W poniższym przykładzie pokazano, że mimo pomyślnego dopasowania pusta grupa przechwytywania znajduje się między dwoma udanymi grupami przechwytywania.

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
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

Zobacz też