Sdílet prostřednictvím


Konstrukce zpětných odkazů v regulárních výrazech

Zpětné odvozování představují pohodlný způsob identifikace opakovaného znaku nebo podřetězce v řetězci. Pokud například vstupní řetězec obsahuje více výskytů libovolného podřetězce, můžete se shodovat s prvním výskytem se skupinou zachycení a pak pomocí zpětného odvozování spárovat následné výskyty podřetězce.

Poznámka:

Samostatná syntaxe se používá k odkazování na pojmenované a číslované zachycené skupiny v náhradních řetězcích. Další informace naleznete v tématu Nahrazení.

.NET definuje samostatné jazykové prvky, které odkazují na číslovaný a pojmenovaný záznam skupin. Další informace o zachytávání skupin naleznete v tématu Seskupování konstruktorů.

Číslovaný backreference

Číslovaný backreference používá následující syntaxi:

\číslo

where number is the ordinal position of the capturing group in the regular expression. \4 Například odpovídá obsahu čtvrté skupiny zachycení. Pokud číslo není definováno ve vzoru regulárního výrazu, dojde k chybě analýzy a modul regulárních výrazů vyvolá chybu ArgumentException. Například regulární výraz \b(\w+)\s\1 je platný, protože (\w+) je první a pouze zachytává skupinu ve výrazu. Na druhou stranu je \b(\w+)\s\2 neplatná a vyvolá výjimku argumentu, protože neexistuje žádné zachytávání skupin číslování \2. Kromě toho, pokud číslo identifikuje zachytávání skupiny v určité pořadové pozici, ale tato zachytávání skupiny byla přiřazena číselný název jiný než jeho pořadová pozice, analyzátor regulárních výrazů ArgumentExceptiontaké vyvolá .

Všimněte si nejednoznačnosti mezi osmičkovými řídicími kódy (například \16) a \zpětnými odvozováním čísel , které používají stejnou notaci. Tato nejednoznačnost se vyřeší následujícím způsobem:

  • Výrazy \1 se \9 vždy interpretují jako zpětné odvozování a ne jako osmičkové kódy.

  • Pokud je první číslice výrazu multidigit 8 nebo 9 (například \80 nebo \91), výraz interpretovaný jako literál.

  • Výrazy z \10 a vyšší se považují za zpětné odvozování, pokud je k číslu odpovídající zpětné odvozování. V opačném případě se interpretují jako osmičkové kódy.

  • Pokud regulární výraz obsahuje zpětné odvozování na nedefinované číslo skupiny, dojde k chybě analýzy a modul regulárních výrazů ArgumentExceptionvyvolá chybu .

Pokud je nejednoznačnost problémem, můžete použít \k<notaci názvu> , která je jednoznačná a nelze ji zaměňovat s osmičkovými kódy znaků. Podobně šestnáctkové kódy, jako \xdd jsou jednoznačné, a nelze je zaměňovat s backreferencemi.

Následující příklad najde v řetězci dvojité znaky slova. Definuje regulární výraz, (\w)\1který se skládá z následujících prvků.

Element (Prvek) Popis
(\w) Porovná znak slova a přiřadí ho první zachycené skupině.
\1 Porovná další znak, který je stejný jako hodnota první zachycené skupiny.
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.

Pojmenované zpětné odvozy

Pojmenovaná backreference je definována pomocí následující syntaxe:

\k<Jméno >

nebo:

\k'Jméno '

where name is the name of a capturing group defined in the regular expression pattern. Pokud název není definován v vzoru regulárního výrazu, dojde k chybě analýzy a modul regulárních výrazů ArgumentExceptionvyvolá chybu .

Následující příklad najde v řetězci dvojité znaky slova. Definuje regulární výraz, (?<char>\w)\k<char>který se skládá z následujících prvků.

Element (Prvek) Popis
(?<char>\w) Porovná znak slova a přiřadí ho k zachycené skupině s názvem char.
\k<char> Porovná další znak, který je stejný jako hodnota zachycené char skupiny.
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.

Pojmenované číselné backreference

V pojmenovaném zpětném odvozování s \knázvem může být také řetězcová reprezentace čísla. Například následující příklad používá regulární výraz (?<2>\w)\k<2> k vyhledání dvouslovných znaků v řetězci. V tomto případě příklad definuje zachytávání skupiny, která má explicitně název "2", a backreference má odpovídající název "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.

Pokud je název řetězcovou reprezentací čísla a žádná zachycená skupina nemá tento název,\k< název> je stejný jako číslo zpětného odvozování\, kde číslo je pořadová pozice zachycení. V následujícím příkladu existuje jedna zachycená skupina s názvem char. Konstruktor backreference odkazuje na něj jako \k<1>. Jak ukazuje výstup z příkladu, volání úspěšné Regex.IsMatch , protože char je první zachytácí skupina.

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

Pokud je však název řetězcovou reprezentací čísla a zachytávací skupina v této pozici byla explicitně přiřazena číselný název, analyzátor regulárních výrazů nemůže identifikovat zachytávací skupinu podle jeho pořadové pozice. Místo toho vyvolá .ArgumentException Jediná skupina zachycení v následujícím příkladu má název "2". Vzhledem k tomu, že se \k konstruktor používá k definování zpětného odvozování s názvem "1", parser regulárních výrazů nemůže identifikovat první zachytávací skupinu a vyvolá výjimku.

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

Co se shoduje s backreferencemi

Zpětné odvozování odkazuje na nejnovější definici skupiny (při porovnávání zleva nejvíce vlevo). Když skupina provede více zachycení, zpětné odvození odkazuje na nejnovější zachycení.

Následující příklad obsahuje vzor regulárního výrazu, (?<1>a)(?<1>\1b)*který předefinuje pojmenovanou skupinu \1. Následující tabulka popisuje jednotlivé vzory v regulárním výrazu.

Vzor Popis
(?<1>a) Porovná znak "a" a přiřadí výsledek skupině zachycení s názvem 1.
(?<1>\1b)* Porovná nula nebo více výskytů skupiny s názvem 1 "b" a přiřadí výsledek k zachycené skupině s názvem 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

Při porovnávání regulárního výrazu se vstupním řetězcem ("aababb") modul regulárních výrazů provádí následující operace:

  1. Začíná na začátku řetězce a úspěšně odpovídá "a" s výrazem (?<1>a). Hodnota 1 skupiny je teď "a".

  2. Přejde na druhý znak a úspěšně odpovídá řetězci "ab" s výrazem \1b, nebo "ab". Pak přiřadí výsledek "ab" k \1.

  3. Přejde na čtvrtý znak. Výraz (?<1>\1b)* se musí shodovat s nulou nebo vícekrát, takže se úspěšně shoduje s řetězcem "abb" s výrazem \1b. Přiřadí výsledek "abb", zpět .\1

V tomto příkladu je kvantifikátor smyček – vyhodnocuje se opakovaně, * dokud modul regulárních výrazů neodpovídá vzoru, který definuje. Kvantifikátory smyček nesmažou definice skupin.

Pokud skupina nezachytá žádné podřetězce, není definováno zpětné odvozování této skupiny a nikdy se neshoduje. To je znázorněno vzorem \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\bregulárního výrazu , který je definován takto:

Vzor Popis
\b Zahajte shodu na hranici slova.
(\p{Lu}{2}) Porovná dvě velká písmena. Toto je první zachytávající skupina.
(\d{2})? Porovná žádný nebo jeden výskyt dvou desetinných číslic. Toto je druhá zachytávající skupina.
(\p{Lu}{2}) Porovná dvě velká písmena. Toto je třetí zachytávající skupina.
\b Ukončete shodu na hranici slova.

Vstupní řetězec se může shodovat s tímto regulárním výrazem, i když nejsou přítomny dvě desetinné číslice definované druhou zachytáváním. Následující příklad ukazuje, že i když je shoda úspěšná, je mezi dvěma úspěšnými skupinami zachycení nalezena prázdná skupina zachycení.

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

Viz také