Пример регулярного выражения: сканирование для HREFs

В следующем примере показаны поиск и вывод всех значений href="...", а также их позиций в строке.

Предупреждение

При использовании System.Text.RegularExpressions для обработки ненадежных входных данных передайте время ожидания. Злоумышленник может предоставить входные данные RegularExpressions, вызывая атаку типа "отказ в обслуживании". API платформы ASP.NET Core, использующие RegularExpressions, передают время ожидания.

Объект Regex

Поскольку метод DumpHRefs может быть вызван из пользовательского кода несколько раз, он использует метод static (Shared в Visual Basic) Regex.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]+)) показаны в следующей таблице.

Расписание Description
href Совпадение с литеральной строкой "href". Сопоставление не учитывает регистр.
\s* Соответствует нулю или нескольким символам пробела.
= Соответствует знаку равенства.
\s* Соответствует нулю или нескольким символам пробела.
(?: Запустите группу без записи.
["'](?<1>[^"']*)["'] Сопоставляйте кавычки или апостроф, за которым следует группа захвата, которая соответствует любому символу, отличному от кавычки или апострофа, за которым следует кавычка или апостроф. В этот шаблон включена группа с именем 1.
| Логический или соответствующий предыдущему выражению или следующему выражению.
(?<1>[^>\s]+) Записывая группа, использующая негритированный набор для сопоставления любого символа, отличного от знака больше или символа пробела. В этот шаблон включена группа с именем 1.
) Завершите группу без записи.

Класс результатов сопоставления

Результаты поиска сохраняются в классе Match, который предоставляет доступ ко всем подстрокам, извлеченным в ходе поиска. Также он запоминает искомую строку и использованное регулярное выражение, что позволяет вызвать метод Match.NextMatch для продолжения поиска с того места, где закончился предыдущий.

Явно именованные записи

В обычных регулярных выражениях круглые скобки, обозначающие отдельные шаблоны, автоматически последовательно нумеруются. В связи с этим возникают две проблемы. Во-первых, если регулярное выражение изменяется из-за вставки или удаления пары круглых скобок, все части кода, которые ссылаются на нумерованные шаблоны, необходимо переписать, чтобы отразить новую нумерацию. Во-вторых, вследствие того, что различные пары круглых скобок часто используются для определения двух альтернативных выражений для поиска, трудно определить, какое из двух выражений в действительности вернуло результат.

Для решения этих проблем класс Regex поддерживает синтаксис (?<name>…), с помощью которого найденное совпадение можно сохранить в указанной ячейке (которой можно присвоить строковое имя или целочисленное обозначение; числа при этом работают быстрее). Таким образом, если совпадение будет найдено повторно, оно попадет в ту же ячейку. В случае конфликта успешным является то совпадение, которое было помещено в ячейку последним. (Однако доступен полный список нескольких совпадений для одного слота. Дополнительные сведения см. в Group.Captures коллекции.)

См. также