正则表达式示例:扫描 HREF

下面的示例搜索输入字符串并显示所有 href="…" 的值和它们在字符串中的位置。

警告

如果使用 System.Text.RegularExpressions 处理不受信任的输入,则传递一个超时。 恶意用户可能会向 RegularExpressions 提供输入,从而导致拒绝服务攻击。 使用 RegularExpressions 的 ASP.NET Core 框架 API 会传递一个超时。

Regex 对象

因为可以通过用户代码多次调用 DumpHRefs 方法,所以它使用 static(Visual Basic 中的 SharedRegex.Match(String, String, RegexOptions) 方法。 这样一来,正则表达式引擎不仅可以缓存正则表达式,还杜绝了每次调用方法时实例化新 Regex 对象产生的开销。 随后使用 Match 对象循环访问字符串中的所有匹配。

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

下面的示例演示对 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

正则表达式模式 href\s*=\s*(?:["'](?<1>[^"']*)["']|(?<1>[^>\s]+)) 的含义如下表所示。

模式 描述
href 匹配文本字符串“href”。 匹配不区分大小写。
\s* 匹配零个或多个空白字符。
= 匹配等于号。
\s* 匹配零个或多个空白字符。
(?: 启动非捕获组。
["'](?<1>[^"']*)["'] 匹配一个引号或单引号,后跟一个匹配除引号或单引号以外的其他任意字符的捕获组,然后再后跟一个引号或单引号。 名为 1 的组包含在此模式。
| Boolean OR,它匹配上一个表达式或下一个表达式。
(?<1>[^>\s]+) 捕获组,它使用否定集来匹配大于号或空格字符以外的其他任何字符。 名为 1 的组包含在此模式。
) 结束非捕获组。

匹配结果类

搜索结果存储在 Match 类中,此类可访问搜索提取的所有子字符串。 它还会记住搜索的字符串和使用的正则表达式,因此可以调用 Match.NextMatch 方法,从上一次搜索结束的位置开始执行另一次搜索。

显式命名的捕获

在传统正则表达式中,捕获圆括号会自动按顺序编号。 这会导致两个问题。 首先,如果通过插入或删除一组圆括号修改正则表达式,则必须重新编写引用带编号捕获的所有代码才能反映新编号。 其次,由于不同的圆括号组通常用于为可接受的匹配项提供两个替代表达式,则可能难以确定这两个表达式中的哪个表达式实际返回了结果。

为了解决这些问题,Regex 类支持语法 (?<name>…),以便将匹配捕获到指定槽中(可以使用字符串或整数命名槽;如果使用整数命名,可以更快地召回)。 因此,相同字符串的替代匹配全都可以定向到相同位置。 发生冲突时,放入槽中的最后一个匹配项是成功的匹配项。 (但是,提供了适用于单个槽的多个匹配项的完整列表。有关详细信息,请参阅 Group.Captures 集合。)

请参阅