正規表現の例: HREF のスキャン

次の例では、入力文字列を検索して、文字列中のすべての href="…" 値とその場所を表示します。

警告

System.Text.RegularExpressions を使用して信頼できない入力を処理するときは、タイムアウトを渡します。 悪意のあるユーザーが RegularExpressions に入力を提供して、サービス拒否攻撃を行う可能性があります。 RegularExpressions を使用する ASP.NET Core フレームワーク API は、タイムアウトを渡します。

Regex オブジェクト

DumpHRefs メソッドは、ユーザー コードから複数回呼び出される可能性があるため、static (Visual Basic の場合は Shared) 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]+)) の解釈を次の表に示します。

パターン 説明
href リテラル文字列 "href" と一致します。 一致では、大文字と小文字を区別しません。
\s* 0 個以上の空白文字と一致します。
= 等号と一致します。
\s* 0 個以上の空白文字と一致します。
(?: 非キャプチャ グループを開始します。
["'](?<1>[^"']*)["'] 引用符またはアポストロフィ、引用符またはアポストロフィ以外の任意の文字と一致するキャプチャ グループ、引用符またはアポストロフィの順に続く文字列。 このパターンには 1 という名前のグループが含まれています。
| 前の式または次の式のいずれかに一致するブール値 OR。
(?<1>[^>\s]+) 符号を否定したセットを使用して、大なり記号または空白文字以外の任意の文字と一致するキャプチャグループ。 このパターンには 1 という名前のグループが含まれています。
) 非キャプチャ グループを終了します。

結果クラスと一致する

検索結果は Match クラス内に格納されます。これにより、検索処理によって抽出されたすべての部分文字列にアクセスできます。 このクラスは、検索対象となった文字列や、使用された正規表現も記憶しているため、Match.NextMatch メソッドを呼び出して、最後の検索が終了した位置から別の検索を実行することができます。

明示的に指定したキャプチャ

従来の正規表現では、キャプチャするかっこに自動的に連番が割り当てられます。 その結果、2 つの問題が発生します。 1 つ目の問題は、かっこのペアの挿入や削除を行って正規表現が修正されると、新しい番号を反映するために、番号付きのキャプチャを参照するコードをすべて書き直す必要があることです。 2 つ目の問題は、ある文字列の検索用に 2 つの代替表現を指定する際、異なるかっこのペアを使用することが多いため、実際にどちらの代替表現から結果が返されたのかを判断するのが難しい場合があることです。

これらの問題に対処するために、Regex クラスでは指定されたスロットに一致文字列をキャプチャするための構文 (?<name>…) をサポートしています (スロットには、文字列または整数の名前を付けることができます。整数の名前を付けた方が、よりすばやく再呼び出しできます)。 これにより、同じ文字列に対する代替表現の一致結果をすべて同じ場所に渡すことができます。 競合が発生する場合は、スロットにキャプチャされた最後の一致文字列が、適切な一致であると見なされます。 (ただし、1 つのスロットで複数の一致文字列の完全なリストを使用することもできます。詳細については、Group.Captures コレクションを参照してください。)

関連項目