Exemplo de expressão regular: Verificação de HREFs

O exemplo a seguir procura uma cadeia de caracteres de entrada e exibe todos os valores href="…" e suas localizações na cadeia de caracteres.

Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis, passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para RegularExpressions, causando um ataque de negação de serviço. APIs ASP.NET Core Framework que usam RegularExpressions passam um tempo limite.

O objeto Regex

Como o método DumpHRefs pode ser chamado várias vezes do código do usuário, ele usa o método static (Shared no Visual Basic) Regex.Match(String, String, RegexOptions). Isso permite que o mecanismo de expressões regulares armazene em cache a expressão regular e evite a sobrecarga de instanciar um novo objeto Regex sempre que o método é chamado. Um objeto Match é usado para iterar por todas as correspondências na cadeia de caracteres.

private static void DumpHRefs(string inputString)
{
    string hrefPattern = @"href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>[^>\s]+))";

    try
    {
        Match regexMatch = Regex.Match(inputString, hrefPattern,
                                       RegexOptions.IgnoreCase | RegexOptions.Compiled,
                                       TimeSpan.FromSeconds(1));
        while (regexMatch.Success)
        {
            Console.WriteLine($"Found href {regexMatch.Groups[1]} at {regexMatch.Groups[1].Index}");
            regexMatch = regexMatch.NextMatch();
        }
    }
    catch (RegexMatchTimeoutException)
    {
        Console.WriteLine("The matching operation timed out.");
    }
}
Private Sub DumpHRefs(inputString As String)
    Dim hrefPattern As String = "href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>[^>\s]+))"

    Try
        Dim regexMatch = Regex.Match(inputString, hrefPattern,
                                     RegexOptions.IgnoreCase Or RegexOptions.Compiled,
                                     TimeSpan.FromSeconds(1))
        Do While regexMatch.Success
            Console.WriteLine($"Found href {regexMatch.Groups(1)} at {regexMatch.Groups(1).Index}.")
            regexMatch = regexMatch.NextMatch()
        Loop
    Catch e As RegexMatchTimeoutException
        Console.WriteLine("The matching operation timed out.")
    End Try
End Sub

O exemplo a seguir mostra uma chamada para o método DumpHRefs.

public static void Main()
{
    string inputString = "My favorite web sites include:</P>" +
                         "<A HREF=\"https://learn.microsoft.com/en-us/dotnet/\">" +
                         ".NET Documentation</A></P>" +
                         "<A HREF=\"http://www.microsoft.com\">" +
                         "Microsoft Corporation Home Page</A></P>" +
                         "<A HREF=\"https://devblogs.microsoft.com/dotnet/\">" +
                         ".NET Blog</A></P>";
    DumpHRefs(inputString);
}
// The example displays the following output:
//       Found href https://learn.microsoft.com/dotnet/ at 43
//       Found href http://www.microsoft.com at 114
//       Found href https://devblogs.microsoft.com/dotnet/ at 188
Public Sub Main()
    Dim inputString As String = "My favorite web sites include:</P>" &
                                "<A HREF=""https://learn.microsoft.com/en-us/dotnet/"">" &
                                ".NET Documentation</A></P>" &
                                "<A HREF=""http://www.microsoft.com"">" &
                                "Microsoft Corporation Home Page</A></P>" &
                                "<A HREF=""https://devblogs.microsoft.com/dotnet/"">" &
                                ".NET Blog</A></P>"
    DumpHRefs(inputString)
End Sub
' The example displays the following output:
'       Found href https://learn.microsoft.com/dotnet/ at 43
'       Found href http://www.microsoft.com at 114
'       Found href https://devblogs.microsoft.com/dotnet/ at 188

O padrão da expressão regular href\s*=\s*(?:["'](?<1>[^"']*)["']|(?<1>[^>\s]+)) é interpretado conforme mostrado na tabela a seguir.

Padrão Descrição
href Corresponder à cadeia de caracteres literal “href”. A correspondência não diferencia maiúsculas de minúsculas.
\s* Corresponder a zero ou mais caracteres de espaço em branco.
= Corresponder ao sinal de igual.
\s* Corresponder a zero ou mais caracteres de espaço em branco.
(?: Inicie um grupo sem captura.
["'](?<1>[^"']*)["'] Combine um sinal de aspas ou apóstrofo, seguido por um grupo de captura que corresponda a qualquer caractere que não um de aspas ou apóstrofo, seguido por um sinal de aspas ou apóstrofo. O grupo chamado 1 está incluído nesse padrão.
| OR booliano que corresponde à expressão anterior ou seguinte.
(?<1>[^>\s]+) Um grupo de captura que usa um conjunto negado para corresponder a qualquer caractere diferente de um sinal maior que um caractere de espaço em branco. O grupo chamado 1 está incluído nesse padrão.
) Encerre o grupo sem captura.

Classe de resultado de correspondência

Os resultados de uma pesquisa são armazenados na classe Match, que fornece acesso a todas as subcadeias de caracteres extraídas pela pesquisa. Também lembra a cadeia de caracteres que está sendo pesquisada e a expressão regular que está sendo usada para poder chamar o método Match.NextMatch para executar outra pesquisa iniciando onde a última terminou.

Capturas nomeadas explicitamente

Em expressões regulares tradicionais, os parênteses de captura são numerados sequencialmente de forma automática. Isso causa dois problemas. Primeiro, se uma expressão regular for modificada pela inserção ou remoção de um conjunto de parênteses, todo o código que se refere às captura numeradas deverá ser reescrito para refletir a nova numeração. Em segundo lugar, como diferentes conjuntos de parênteses geralmente são usados para fornecer duas expressões alternativas para uma correspondência aceitável, pode ser difícil determinar qual das duas expressões realmente retornou um resultado.

Para resolver esses problemas, a classe Regex dá suporte à sintaxe (?<name>…) para capturar uma correspondência em um slot especificado (o slot pode ser nomeado usando uma cadeia de caracteres ou um inteiro; inteiros podem ser recuperados mais rapidamente). Assim, todas as correspondências alternativas para a mesma cadeia de caracteres podem ser direcionadas para o mesmo local. Em caso de conflito, a última correspondência solta em um slot é a correspondência com êxito. (No entanto, está disponível uma lista completa de diversas correspondências para um único slot. Confira a coleção Group.Captures para detalhes.)

Confira também