Rückverweiskonstrukte in regulären Ausdrücken

Rückverweise bieten eine einfache Möglichkeit, ein wiederholtes Zeichen oder eine Teilzeichenfolge innerhalb einer Zeichenfolge zu identifizieren. Wenn z.B. die Eingabezeichenfolge mehrere Vorkommen einer beliebigen Teilzeichenfolge enthält, können Sie das erste Vorkommen mit einer Erfassungsgruppe abgleichen und dann mit einem Rückverweis nachfolgende Vorkommen der Teilzeichenfolge abgleichen.

Hinweis

Eine separate Syntax wird verwendet, um auf benannte und nummerierte Erfassungsgruppen in Ersetzungszeichenfolgen zu verweisen. Weitere Informationen finden Sie unter Ersetzungen in regulären Ausdrücken.

.NET definiert separate Sprachelemente, um auf nummerierte und benannte Erfassungsgruppen zu verweisen. Weitere Informationen zu Erfassungsgruppen finden Sie unter Gruppierungskonstrukte in regulären Ausdrücken.

Nummerierte Rückverweise

Ein nummerierter Rückverweis verwendet die folgende Syntax:

\Zahl

wobei Nummer die Ordnungsposition der Erfassungsgruppe im regulären Ausdruck ist. \4 gleicht z.B. den Inhalt der vierten Erfassungsgruppe ab. Wenn number nicht im Muster eines regulären Ausdrucks definiert ist, tritt ein Analysefehler auf, und die Engine für reguläre Ausdrücke löst eine ArgumentException aus. Beispielsweise ist der reguläre Ausdruck \b(\w+)\s\1 gültig, da (\w+) die erste und einzige Erfassungsgruppe im Ausdruck ist. Auf der anderen Seite ist \b(\w+)\s\2 ungültig und löst eine Argumentausnahme aus, da es keine nummerierte Erfassungsgruppe \2 gibt. Wenn darüber hinaus number eine Erfassungsgruppe an einer bestimmten Ordnungsposition identifiziert, dieser Erfassungsgruppe jedoch ein anderer Name als die zugehörige Ordnungsposition zugewiesen wurde, löst der Parser für reguläre Ausdrücke ebenfalls eine ArgumentException aus.

Beachten Sie die Mehrdeutigkeit zwischen oktalen Escapesequenzen (z.B. \16) und \number-Rückverweisen mit der gleichen Notation. Diese Mehrdeutigkeit wird wie folgt aufgelöst:

  • Die Ausdrücke \1 bis \9 werden immer als Rückverweise und nicht als oktale Codes interpretiert.

  • Wenn die erste Ziffer eines mehrziffrigen Ausdrucks 8 oder 9 ist (z.B. \80 oder \91), wird der Ausdruck als Literal interpretiert.

  • Ausdrücke aus \10 und höher werden als Rückverweise betrachtet, wenn ein Rückverweis vorhanden ist, der dieser Nummer entspricht; andernfalls werden sie als oktale Codes interpretiert.

  • Wenn ein regulärer Ausdruck einen Rückverweis auf eine nicht definierte Gruppennummer enthält, tritt ein Analysefehler auf, und die Engine für reguläre Ausdrücke löst eine ArgumentException aus.

Wenn die Mehrdeutigkeit ein Problem ist, können Sie die Notation \k<name> verwenden. Sie ist eindeutig und kann nicht mit oktalen Zeichencodes verwechselt werden. Auf ähnliche Weise sind hexadezimale Codes wie z.B. \xdd eindeutig und können nicht mit Rückverweisen verwechselt werden.

Im folgenden Beispiel werden doppelte Wortzeichen in einer Zeichenfolge gesucht. Ein regulärer Ausdruck (\w)\1 wird definiert, der aus den folgenden Elementen besteht.

Element BESCHREIBUNG
(\w) Übereinstimmung mit einem Wortzeichen und dessen Zuweisung zur ersten Erfassungsgruppe.
\1 Übereinstimmung mit dem nächsten Zeichen, das mit dem Wert der ersten Erfassungsgruppe identisch ist.
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.

Benannte Rückverweise

Ein benannter Rückverweis wird mit der folgenden Syntax definiert:

\k<Name>

oder:

\k'Name'

wobei Name der Name einer Erfassungsgruppe ist, die im Muster eines regulären Ausdrucks definiert ist. Wenn name nicht im Muster eines regulären Ausdrucks definiert ist, tritt ein Analysefehler auf, und die Engine für reguläre Ausdrücke löst eine ArgumentException aus.

Im folgenden Beispiel werden doppelte Wortzeichen in einer Zeichenfolge gesucht. Ein regulärer Ausdruck (?<char>\w)\k<char> wird definiert, der aus den folgenden Elementen besteht.

Element BESCHREIBUNG
(?<char>\w) Übereinstimmung mit einem Wortzeichen und dessen Zuweisung zu einer Erfassungsgruppe mit dem Namen char
\k<char> Übereinstimmung mit dem nächsten Zeichen, das mit dem Wert der char-Erfassungsgruppe identisch ist
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.

Benannte numerische Rückverweise

In einem benannten Rückverweis mit \k kann name auch die Zeichenfolgendarstellung einer Zahl sein. Im folgenden Beispiel werden mit dem regulären Ausdruck (?<2>\w)\k<2> doppelte Wortzeichen in einer Zeichenfolge gesucht. In diesem Fall definiert das Beispiel eine Erfassungsgruppe, die explizit als „2“ benannt wurde. Der Rückverweis wurde entsprechend ebenfalls „2“ genannt.

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.

Wenn name die Zeichenfolgendarstellung einer Zahl ist und keine Erfassungsgruppe diesen Namen trägt, ist \k<name> das Gleiche wie der Rückverweis \number, wobei number die Ordnungsposition der Erfassung ist. Im folgenden Beispiel gibt es eine einzige Erfassungsgruppe namens char. Das Rückverweiskonstrukt referenziert die Gruppe als \k<1>. Wie die Ausgabe des Beispiels zeigt, ist der Aufruf von Regex.IsMatch erfolgreich, weil char die erste Erfassungsgruppe ist.

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

Wenn jedoch name die Zeichenfolgendarstellung einer Zahl ist und der Erfassungsgruppe an dieser Position explizit ein numerischer Name zugewiesen wurde, kann der Parser für reguläre Ausdrücke die Erfassungsgruppe nicht anhand ihrer Ordnungsposition identifizieren. Stattdessen wird ArgumentException ausgelöst. Die einzige Erfassungsgruppe im folgenden Beispiel ist „2“. Da das \k-Konstrukt verwendet wird, um einen Rückverweis namens „1“ zu definieren, kann der Parser für reguläre Ausdrücke die erste Erfassungsgruppe nicht identifizieren und löst eine Ausnahme aus.

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

Übereinstimmende Rückverweise

Ein Rückverweis bezieht sich auf die aktuellste Definition einer Gruppe (beim Abgleichen von links nach rechts die am weitesten links befindliche Definition). Wenn eine Gruppe mehrere Erfassungen durchführt, bezieht sich ein Rückverweis auf die aktuellste Erfassung.

Das folgende Beispiel enthält ein Muster für reguläre Ausdrücke, (?<1>a)(?<1>\1b)*, das die Gruppe namens „\1“ neu definiert. Die folgende Tabelle beschreibt jedes Muster im regulären Ausdruck.

Muster BESCHREIBUNG
(?<1>a) Übereinstimmung mit dem Zeichen „a“ und Zuweisen des Ergebnisses zur Erfassungsgruppe 1
(?<1>\1b)* Übereinstimmung mit null oder einem Vorkommen der Gruppe 1 zusammen mit einem „b“ und Zuweisen des Ergebnisses zur Erfassungsgruppe 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

Beim Vergleich des regulären Ausdrucks mit der Eingabezeichenfolge („aababb“) führt die Engine für reguläre Ausdrücke die folgenden Vorgänge aus:

  1. Begonnen wird am Anfang der Zeichenfolge, und „a“ wird erfolgreich mit dem Ausdruck (?<1>a) abgeglichen. Der Wert der Gruppe 1 ist nun „a“.

  2. Es wird zum zweiten Zeichen gewechselt, und die Zeichenfolge „ab“ wird erfolgreich mit dem Ausdruck \1b bzw. „ab“ abgeglichen. Anschließend wird \1 das Ergebnis „ab“ zugewiesen.

  3. Es wird zum vierten Zeichen gewechselt. Der Ausdruck (?<1>\1b)* muss nullmal oder mehrfach abgeglichen werden, damit er der Zeichenfolge „abb“ mit dem Ausdruck \1b entspricht. Das Ergebnis „abb“ wird wieder \1 zugewiesen.

In diesem Beispiel ist * ein Schleifenquantifizierer – er wird wiederholt ausgewertet, bis die Engine für reguläre Ausdrücke keine Entsprechung für das Muster finden kann, das sie definiert. Quantifizierer, die in Schleifen durchlaufen werden, löschen keine Gruppendefinitionen.

Wurden durch eine Gruppe keine Teilzeichenfolgen gefunden, ist der Rückverweis auf diese Gruppe nicht definiert und führt niemals zu einer Übereinstimmung. Dies wird durch das Muster des regulären Ausdrucks \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b veranschaulicht, das folgendermaßen definiert ist:

Muster Beschreibung
\b Beginnt den Vergleich an einer Wortgrenze.
(\p{Lu}{2}) Übereinstimmung mit zwei Großbuchstaben. Dies ist die erste Erfassungsgruppe.
(\d{2})? Übereinstimmung mit null oder einem Vorkommen von zwei Dezimalziffern. Dies ist die zweite Erfassungsgruppe.
(\p{Lu}{2}) Übereinstimmung mit zwei Großbuchstaben. Dies ist die dritte Erfassungsgruppe.
\b Beendet den Vergleich an einer Wortgrenze.

Eine Eingabezeichenfolge kann auch dann mit diesem regulären Ausdruck übereinstimmen, wenn die von der zweiten Erfassungsgruppe definierten zwei Dezimalziffern nicht vorhanden sind. Das folgende Beispiel zeigt, dass trotz Übereinstimmung eine leere Erfassungsgruppe zwischen zwei erfolgreichen Erfassungsgruppen gefunden wird.

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

Siehe auch