Backreference-constructies in reguliere expressies

Backreferences bieden een handige manier om een herhaald teken of subtekenreeks binnen een tekenreeks te identificeren. Als de invoertekenreeks bijvoorbeeld meerdere exemplaren van een willekeurige subtekenreeks bevat, kunt u het eerste exemplaar vergelijken met een vastleggende groep en vervolgens een backreference gebruiken om de volgende exemplaren van de subtekenreeks te vinden.

Notitie

Er wordt een afzonderlijke syntaxis gebruikt om te verwijzen naar benoemde en genummerde vastleggen van groepen in vervangende tekenreeksen. Zie Vervangingen voor meer informatie.

.NET definieert afzonderlijke taalelementen om te verwijzen naar genummerde en benoemde vastleggende groepen. Zie Groeperingsconstructies voor meer informatie over het vastleggen van groepen.

Genummerde backreferences

Een genummerde backreference maakt gebruik van de volgende syntaxis:

\Nummer

waarbij getal de rangschikkingspositie is van de vastleggende groep in de reguliere expressie. Komt bijvoorbeeld \4 overeen met de inhoud van de vierde opnamegroep. Als getal niet is gedefinieerd in het reguliere expressiepatroon, treedt er een parseringsfout op en genereert de reguliere expressie-engine een ArgumentException. De reguliere expressie \b(\w+)\s\1 is bijvoorbeeld geldig, omdat (\w+) dit de eerste en alleen de groep in de expressie is. Aan de andere kant \b(\w+)\s\2 is deze ongeldig en genereert een argumentuitzondering, omdat er geen genummerde groep wordt vastgelegd \2. Als getal bovendien een vastleggende groep in een bepaalde rangschikkingspositie identificeert, maar dat aan die groep een numerieke naam is toegewezen die verschilt van de rangschikkingspositie, genereert de reguliere expressieparser ook een ArgumentException.

Let op de dubbelzinnigheid tussen octale escapecodes (zoals \16) en \getalachterferences die dezelfde notatie gebruiken. Deze dubbelzinnigheid wordt als volgt opgelost:

  • De expressies \1\9 worden altijd geïnterpreteerd als backreferences en niet als octale codes.

  • Als het eerste cijfer van een expressie met meerderedigit 8 of 9 (zoals \80 of \91) is, wordt de expressie geïnterpreteerd als een letterlijke waarde.

  • Expressies van \10 en hoger worden beschouwd als backreferences als er een backreference is die overeenkomt met dat getal; anders worden ze geïnterpreteerd als octale codes.

  • Als een reguliere expressie een terugverwijzing naar een niet-gedefinieerd groepsnummer bevat, treedt er een parseringsfout op en genereert de reguliere expressie-engine een ArgumentException.

Als de dubbelzinnigheid een probleem is, kunt u de \k<naam> notatie gebruiken, die ondubbelzinnig is en niet kan worden verward met octale tekencodes. Op dezelfde manier zijn hexadecimale codes zoals \xdd ondubbelzinnig en kunnen ze niet worden verward met backreferences.

In het volgende voorbeeld worden dubbele woordtekens in een tekenreeks gevonden. Er wordt een reguliere expressie gedefinieerd, (\w)\1die uit de volgende elementen bestaat.

Element Beschrijving
(\w) Koppel een woordteken en wijs het toe aan de eerste groep die vastlegt.
\1 Komt overeen met het volgende teken dat gelijk is aan de waarde van de eerste groep die de groep vastlegt.
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.

Benoemde backreferences

Een benoemde backreference wordt gedefinieerd met behulp van de volgende syntaxis:

\k<name>

of:

\k'name'

waarbij de naam de naam is van een vastleggende groep die is gedefinieerd in het reguliere expressiepatroon. Als de naam niet is gedefinieerd in het reguliere expressiepatroon, treedt er een parseringsfout op en genereert de engine voor reguliere expressies een ArgumentException.

In het volgende voorbeeld worden dubbele woordtekens in een tekenreeks gevonden. Er wordt een reguliere expressie gedefinieerd, (?<char>\w)\k<char>die uit de volgende elementen bestaat.

Element Beschrijving
(?<char>\w) Koppel een woordteken en wijs het toe aan een vastleggende groep met de naam char.
\k<char> Komt overeen met het volgende teken dat gelijk is aan de waarde van de char vastleggende groep.
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.

Benoemde numerieke backreferences

In een benoemde backreference met \kkan de naam ook de tekenreeksweergave van een getal zijn. In het volgende voorbeeld wordt bijvoorbeeld de reguliere expressie (?<2>\w)\k<2> gebruikt om dubbele woordtekens in een tekenreeks te zoeken. In dit geval definieert het voorbeeld een vastleggende groep die expliciet '2' heet en de backreference overeenkomt met '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.

Als de naam de tekenreeksweergave van een getal is en er geen vastleggende groep die naam heeft, \k<is de naam> hetzelfde als het achterverwijzingsnummer\, waarbij het getal de rangschikkingspositie van de opname is. In het volgende voorbeeld is er één vastleggende groep met de naam char. De backreference-constructie verwijst ernaar als \k<1>. Zoals de uitvoer uit het voorbeeld laat zien, slaagt de aanroep naar de Regex.IsMatch geslaagde, omdat char dit de eerste groep is die de groep vastlegt.

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

Als de naam echter de tekenreeksweergave is van een getal en de vastleggende groep in die positie expliciet een numerieke naam is toegewezen, kan de reguliere expressieparser de vastleggende groep niet identificeren op basis van de rangschikkingspositie. In plaats daarvan gooit het een ArgumentException. De enige opnamegroep in het volgende voorbeeld heet '2'. Omdat de constructie wordt gebruikt om een backreference met de \k naam 1 te definiëren, kan de parser van de reguliere expressie de eerste groep voor vastleggen niet identificeren en wordt er een uitzondering gegenereerd.

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

Welke backreferences komen overeen

Een backreference verwijst naar de meest recente definitie van een groep (de definitie die het meest direct aan de linkerkant wordt gebruikt, wanneer deze overeenkomt met links naar rechts). Wanneer een groep meerdere opnamen maakt, verwijst een backreference naar de meest recente opname.

Het volgende voorbeeld bevat een patroon voor reguliere expressies, (?<1>a)(?<1>\1b)*waarmee de benoemde \1-groep opnieuw wordt gedefinieerd. In de volgende tabel wordt elk patroon in de reguliere expressie beschreven.

Patroon Beschrijving
(?<1>a) Koppel het teken 'a' en wijs het resultaat toe aan de vastleggende groep met de naam 1.
(?<1>\1b)* Koppel nul of meer exemplaren van de groep met de naam 1 'b' en wijs het resultaat toe aan de vastleggende groep met de naam 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

Bij het vergelijken van de reguliere expressie met de invoertekenreeks ('aababb') voert de engine voor reguliere expressies de volgende bewerkingen uit:

  1. Deze begint aan het begin van de tekenreeks en komt overeen met 'a' met de expressie (?<1>a). De waarde van de 1 groep is nu 'a'.

  2. Het gaat naar het tweede teken en komt overeen met de tekenreeks 'ab' met de expressie \1bof 'ab'. Vervolgens wordt het resultaat 'ab' toegewezen aan \1.

  3. Het gaat naar het vierde teken. De expressie (?<1>\1b)* moet nul of meer keren worden vergeleken, dus deze komt overeen met de tekenreeks 'abb' met de expressie \1b. Hiermee wordt het resultaat , 'abb', weer toegewezen aan \1.

In dit voorbeeld * is een lus kwantificator. Deze wordt herhaaldelijk geëvalueerd totdat de engine voor reguliere expressies niet overeenkomt met het patroon dat wordt gedefinieerd. Met lussende kwantificatoren worden groepsdefinities niet gewist.

Als een groep geen subtekenreeksen heeft vastgelegd, is een backreference naar die groep niet gedefinieerd en komt deze nooit overeen. Dit wordt geïllustreerd door het reguliere expressiepatroon \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b, dat als volgt wordt gedefinieerd:

Patroon Beschrijving
\b Begin de overeenkomst op een woordgrens.
(\p{Lu}{2}) Identiek aan twee hoofdletters. Dit is de eerste opnamegroep.
(\d{2})? Kom overeen met nul of één exemplaar van twee decimalen. Dit is de tweede vastleggende groep.
(\p{Lu}{2}) Identiek aan twee hoofdletters. Dit is de derde groep die vastlegt.
\b Beëindig de overeenkomst op een woordgrens.

Een invoertekenreeks kan overeenkomen met deze reguliere expressie, zelfs als de twee decimale cijfers die zijn gedefinieerd door de tweede vastleggende groep niet aanwezig zijn. In het volgende voorbeeld ziet u dat hoewel de overeenkomst is geslaagd, een lege groep voor het vastleggen van groepen wordt gevonden tussen twee geslaagde vastleggende groepen.

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

Zie ook