Costrutti di backreference nelle espressioni regolari
I backreference sono uno strumento utile per identificare un carattere ripetuto o una sottostringa all'interno di una stringa. Se la stringa di input contiene ad esempio più occorrenze di una sottostringa arbitraria, è possibile trovare la prima occorrenza con un gruppo di acquisizione e usare un backreference per trovare le occorrenze successive della sottostringa.
Nota
Per fare riferimento a gruppi di acquisizione denominati e numerati in stringhe sostitutive, si usa una sintassi separata. Per altre informazioni, vedere Sostituzioni.
.NET definisce elementi di linguaggio separati per fare riferimento a gruppi di acquisizione denominati e numerati. Per altre informazioni sui gruppi di acquisizione, vedere Costrutti di raggruppamento.
Backreferenze numerate
Un riferimento all'indietro numerato usa la seguente sintassi:
\
numero
dove numero è la posizione ordinale del gruppo di acquisizione nell'espressione regolare. Ad esempio, \4
corrisponde al contenuto del quarto gruppo di acquisizione. Se il numero non è definito nel modello di espressione regolare, si verifica un errore di parsing e il motore delle espressioni regolari genera un'ArgumentException. Ad esempio, l'espressione regolare \b(\w+)\s\1
è valida, poiché (\w+)
è il primo e l'unico gruppo di acquisizione nell'espressione. D'altra parte, \b(\w+)\s\2
non è un'espressione valida e genera un'eccezione di argomento, perché non esistono gruppi di acquisizione numerati \2
. Inoltre, se numero identifica un gruppo di acquisizione in una determinata posizione ordinale, ma a tale gruppo di acquisizione è stato assegnato un valore numerico diverso dalla relativa posizione ordinale, il parser delle espressioni regolari genera anche un'eccezione ArgumentException.
Si noti l'ambiguità tra i codici di escape ottali, come \16
, e i riferimenti inversi \
numero che usano la stessa notazione. Questa ambiguità viene risolta nel modo seguente:
Le espressioni da
\1
a\9
vengono sempre interpretate come backreference e non come codici ottali.Se la prima cifra di un'espressione composta da più cifre è 8 o 9, ad esempio
\80
o\91
, l'espressione viene interpretata come valore letterale.Le espressioni a partire da
\10
e superiori sono considerate backreference se esiste una corrispondenza per quel numero; altrimenti, vengono interpretate come codici ottali.Se l'espressione regolare contiene un riferimento indietro a un numero di gruppo non definito, si verifica un errore di analisi e il motore delle espressioni regolari genera un'eccezione ArgumentException.
Se il problema è dovuto all'ambiguità, è possibile usare la notazione \k<
nome>
che, non essendo ambigua, non può essere confusa con codici di caratteri ottali. Analogamente, i codici esadecimale come \xdd
non sono ambigui e non possono essere confusi con backreference.
L'esempio seguente consente di trovare caratteri alfanumerici doppi all'interno di una stringa. Viene definita un'espressione regolare (\w)\1
costituita dagli elementi seguenti.
Elemento | Descrizione |
---|---|
(\w) |
Abbina un carattere alfanumerico e assegnalo al primo gruppo di acquisizione. |
\1 |
Trova il prossimo carattere corrispondente al valore del primo gruppo di acquisizione. |
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 '{match.Value}' at position {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.
Riferimenti denominati
Per definire un backreference denominato, usare la sintassi seguente:
\k<
nome>
oppure:
\k'
nome'
dove nome è il nome di un gruppo di acquisizione definito nel modello di espressione regolare. Se nel modello di espressione regolare nome non è definito, si verifica un errore di analisi e il motore delle espressioni regolari genera un'eccezione ArgumentException.
L'esempio seguente consente di trovare caratteri alfanumerici doppi all'interno di una stringa. Viene definita un'espressione regolare (?<char>\w)\k<char>
costituita dagli elementi seguenti.
Elemento | Descrizione |
---|---|
(?<char>\w) |
Trova un carattere di parola e assegnalo a un gruppo di cattura denominato char . |
\k<char> |
Trova il prossimo carattere che è uguale al valore del gruppo di acquisizione char . |
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 '{match.Value}' at position {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.
Backreference numeriche con nome
In un backreference denominato con \k
, nome può anche essere la rappresentazione stringa di un numero. Nell'esempio seguente viene usata l'espressione regolare (?<2>\w)\k<2>
per trovare caratteri alfanumerici doppi all'interno di una stringa. In questo caso, l'esempio definisce un gruppo di acquisizione denominato "2" in modo esplicito e conseguentemente il backreference è denominato "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 '{match.Value}' at position {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.
Se nome è la rappresentazione stringa di un numero e nessun gruppo di acquisizione ha tale nome, \k<
nome>
corrisponde al \
numero del backreference, dove numero è la posizione ordinale dell'acquisizione. Nell'esempio seguente, è presente un solo gruppo di acquisizione denominato char
. Il costrutto del backreference lo chiama \k<1>
. Come dimostra l'output dell'esempio, la chiamata a Regex.IsMatch ha esito positivo perché char
è il primo gruppo di acquisizione.
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
Tuttavia, se nome è la rappresentazione stringa di un numero e al gruppo di acquisizione in tale posizione è stato assegnato in modo esplicito un nome numerico, il parser delle espressioni regolari non è in grado di identificare il gruppo di acquisizione in base alla posizione ordinale. Genera invece una ArgumentException. L'unico gruppo di acquisizione nell'esempio seguente è denominato "2". Dato che il costrutto \k
viene usato per definire un backreference denominato "1", il parser delle espressioni regolari non è in grado di identificare il primo gruppo di acquisizione e genera un'eccezione.
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
Quali backreference corrispondono?
Un backreference si riferisce alla definizione più recente di un gruppo (la definizione immediatamente a sinistra, quando ci si sposta da sinistra a destra). Quando un gruppo esegue più acquisizioni, un backreference fa riferimento all'acquisizione più recente.
L'esempio seguente include un modello di espressione regolare (?<1>a)(?<1>\1b)*
, che ridefinisce il gruppo denominato \1. Nella tabella seguente sono descritti i modelli di espressione regolare.
Modello | Descrizione |
---|---|
(?<1>a) |
Abbina il carattere "a" e assegna il risultato al gruppo di cattura denominato 1 . |
(?<1>\1b)* |
Trova 0 o più occorrenze del gruppo denominato 1 con una "b" e assegna il risultato al gruppo di acquisizione denominato 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
Confrontando l'espressione regolare con la stringa di input ("aababb"), il motore delle espressioni regolari esegue le operazioni seguenti:
Parte dall'inizio della stringa e trova una corrispondenza per "a" con l'espressione
(?<1>a)
. Il valore del1
gruppo è ora "a".Passa al secondo carattere e trova una corrispondenza per la stringa "ab" con l'espressione
\1b
o "ab". Assegna il risultato "ab" a\1
.Passa al quarto carattere. L'espressione
(?<1>\1b)*
deve essere abbinata zero o più volte, quindi corrisponde con successo alla stringa "abb" con l'espressione\1b
. Assegna il risultato "abb" nuovamente a\1
.
In questo esempio *
è un quantificatore di cicli, vale a dire che viene eseguito ripetutamente finché il motore delle espressioni regolari non trova una corrispondenza con il modello che definisce. I quantificatori di cicli non cancellano le definizioni di gruppi.
Se un gruppo non ha acquisito alcuna sottostringa, un riferimento a quel gruppo risulterà indefinito e non corrisponderà mai. Questo comportamento è illustrato dal modello delle espressioni regolari \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b
, definito nel modo seguente:
Modello | Descrizione |
---|---|
\b |
Inizia la corrispondenza al limite di parola. |
(\p{Lu}{2}) |
Abbina due lettere maiuscole. Questo è il primo gruppo di cattura. |
(\d{2})? |
Trova zero o una occorrenza di due cifre decimali. Questo è il secondo gruppo di cattura. |
(\p{Lu}{2}) |
Abbina due lettere maiuscole. Questo è il terzo gruppo di acquisizione. |
\b |
Termina il confronto sul confine di parola. |
Una stringa di input può corrispondere a questa espressione regolare, anche se le due cifre decimali definite dal secondo gruppo di acquisizione non sono presenti. L'esempio seguente illustra che, nonostante una corrispondenza corretta, viene individuato un gruppo di acquisizione vuoto tra due gruppi di acquisizione con corrispondenza corretta.
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 {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 {ctr}: {match.Groups[ctr].Value}");
else
Console.WriteLine($"Group {ctr}: <no match>");
}
}
}
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