Ejemplo de expresiones regulares: Buscar etiquetas HREF

En el ejemplo siguiente se busca una cadena de entrada y se muestran todos los valores href="…" y sus ubicaciones en la cadena.

Advertencia

Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de expiración.

El objeto Regex

Dado que el método DumpHRefs puede llamarse varias veces desde el código de usuario, usa el método static (Shared en Visual Basic) Regex.Match(String, String, RegexOptions). Esto permite que el motor de expresiones regulares almacene en caché la expresión regular y evita la sobrecarga que se produciría al crear instancias de un nuevo objeto Regex cada vez que se llamara al método. Después, se usa un objeto Match para iterar todas las coincidencias de la cadena.

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

En el ejemplo siguiente se muestra la llamada al 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

El patrón de la expresión regular href\s*=\s*(?:["'](?<1>[^"']*)["']|(?<1>[^>\s]+)) se interpreta como se muestra en la tabla siguiente.

Modelo Descripción
href Coincide con la cadena literal "href". La búsqueda no distingue entre mayúsculas y minúsculas.
\s* Busca coincidencias con cero o más caracteres de espacio en blanco.
= Coincide con el signo igual.
\s* Busca coincidencias con cero o más caracteres de espacio en blanco.
(?: Inicie un grupo que no sea de captura.
["'](?<1>[^"']*)["'] Coincidir con una comilla o un apóstrofe, seguido de un grupo de captura que coincida con cualquier carácter que no sea una comilla o un apóstrofe, seguido de una comilla o un apóstrofe. El grupo con nombre 1 se incluye en este patrón.
| OR booleano que coincide con la expresión anterior o la siguiente expresión.
(?<1>[^>\s]+) Un grupo de captura que usa un conjunto negado para que coincida con cualquier carácter distinto de un signo mayor o un carácter de espacio en blanco. El grupo con nombre 1 se incluye en este patrón.
) Inicie un grupo que no sea de captura.

Clase de resultado de coincidencia

Los resultados de la búsqueda se almacenan en la clase Match, que proporciona acceso a todas las subcadenas extraídas por la búsqueda. También recuerda la cadena buscada y la expresión regular que se usa, por lo que puede llamar al método Match.NextMatch para realizar otra búsqueda desde donde terminó la anterior.

Capturas con nombre explícito

En las expresiones regulares tradicionales, los paréntesis de captura se numeran secuencialmente de forma automática. Esto origina dos problemas. En primer lugar, si se modifica una expresión regular al insertar o quitar un conjunto de paréntesis, se debe reescribir todo el código que hace referencia a las capturas numeradas para reflejar la nueva numeración. En segundo lugar, puesto que a menudo se usan diferentes conjuntos de paréntesis para proporcionar expresiones alternativas para una coincidencia aceptable, puede resultar difícil determinar cuál de las dos expresiones devolvió realmente un resultado.

Para abordar estos problemas, la clase Regex admite la sintaxis (?<name>…) para capturar una coincidencia en una ranura especificada (el nombre dado a la ranura puede ser una cadena o un entero; los enteros se pueden recuperar con más rapidez). Así, las coincidencias alternativas para la misma cadena se pueden dirigir todas al mismo lugar. En caso de conflicto, la última coincidencia situada en la ranura es la coincidencia correcta. (Sin embargo, existe una lista completa de coincidencias múltiples para una sola ranura. Consulte la colección Group.Captures para más detalles).

Vea también