.NET Regexで名前付きキャプチャグループにアクセスするにはどうすればよいですか?


255

C#で名前付きキャプチャグループを使用する方法を説明する優れたリソースを見つけるのに苦労しています。これは私がこれまでに持っているコードです:

string page = Encoding.ASCII.GetString(bytePage);
Regex qariRegex = new Regex("<td><a href=\"(?<link>.*?)\">(?<name>.*?)</a></td>");
MatchCollection mc = qariRegex.Matches(page);
CaptureCollection cc = mc[0].Captures;
MessageBox.Show(cc[0].ToString());

ただし、これは常に完全な行を示しています。

<td><a href="/path/to/file">Name of File</a></td> 

さまざまなWebサイトで見つけた他のいくつかの「方法」を試してみましたが、同じ結果が得られ続けています。

正規表現で指定されている名前付きキャプチャグループにアクセスするにはどうすればよいですか?


3
後方参照は、(?<link>。*?)ではなく(?<link>。*)の形式にする必要があります
SO User

11
参考:名前付きのキャプチャグループをxmlファイル内に保存しようとすると、<>が壊れます。(?'link'.*)この場合、代わりに使用できます。この質問に完全に関連しているわけではありませんが、「。
net

1
良い例のStackOverflowリンク:stackoverflow.com/a/1381163/463206 また、@ rtpHarry、いいえ、<>それは壊れません。myRegex.GetGroupNames()コレクションをXML要素名として使用できました。
レーダーボブ

回答:


263

Matchオブジェクトのグループコレクションを使用して、キャプチャグループ名でインデックスを作成します。例:

foreach (Match m in mc){
    MessageBox.Show(m.Groups["link"].Value);
}

10
これはvar mになるので、使用しないでくださいobject
Thomas Weller

111

名前付きキャプチャグループ文字列Groupsは、結果のMatchオブジェクトのプロパティのインデクサーに渡すことで指定します。

ここに小さな例があります:

using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        String sample = "hello-world-";
        Regex regex = new Regex("-(?<test>[^-]*)-");

        Match match = regex.Match(sample);

        if (match.Success)
        {
            Console.WriteLine(match.Groups["test"].Value);
        }
    }
}

10

次のコードサンプルは、間にスペース文字がある場合でもパターンに一致します。すなわち:

<td><a href='/path/to/file'>Name of File</a></td>

と同様:

<td> <a      href='/path/to/file' >Name of File</a>  </td>

メソッドは、入力htmlTd文字列がパターンに一致するかどうかに応じて、trueまたはfalseを返します。一致する場合、outパラメータにはそれぞれリンクと名前が含まれます。

/// <summary>
/// Assigns proper values to link and name, if the htmlId matches the pattern
/// </summary>
/// <returns>true if success, false otherwise</returns>
public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
    link = null;
    name = null;

    string pattern = "<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>";

    if (Regex.IsMatch(htmlTd, pattern))
    {
        Regex r = new Regex(pattern,  RegexOptions.IgnoreCase | RegexOptions.Compiled);
        link = r.Match(htmlTd).Result("${link}");
        name = r.Match(htmlTd).Result("${name}");
        return true;
    }
    else
        return false;
}

私はこれをテストしましたが、正しく動作します。


1
中括弧がグループにアクセスできることを思い出してくれてありがとう。私は${1}物事をより簡単に保つために固執することを好みます。
Magnus Smith

これは質問に完全に回答しますが、ここでは説明するには長すぎる問題がありますが、以下の私の回答で
Mariano Desanze

1

さらに、誰かが正規表現オブジェクトで検索を実行する前にグループ名を必要とするユースケースがある場合、彼は次のように使用できます。

var regex = new Regex(pattern); // initialized somewhere
// ...
var groupNames = regex.GetGroupNames();

1

この回答はRashmi Panditの回答を改善します。これは、質問で詳述されている正確な問題を完全に解決しているように見えるため、他の回答よりも優れています。

悪い点は、非効率的であり、IgnoreCaseオプションを一貫して使用しないことです。

非効率な部分は、正規表現の作成と実行にコストがかかる可能性があるためです。その答えではRegex.IsMatch、正規表現は一度だけ作成された可能性があります(呼び出しは、正規表現を舞台裏で再構築するだけでした)。そして、Matchこの方法は、一回だけと呼ばれ、変数に格納し、その後されている可能性linkname呼ぶべきでResult、その変数から。

また、IgnoreCaseオプションはパーツでのみ使用され、Matchパーツでは使用されませんでしたRegex.IsMatch

また、Regex定義をメソッドの外に移動して、一度だけ構築するようにしました(そのアセンブリをRegexOptions.Compiledオプションで保存している場合、私は賢明なアプローチだと思います)。

private static Regex hrefRegex = new Regex("<td>\\s*<a\\s*href\\s*=\\s*(?:\"(?<link>[^\"]*)\"|(?<link>\\S+))\\s*>(?<name>.*)\\s*</a>\\s*</td>",  RegexOptions.IgnoreCase | RegexOptions.Compiled);

public static bool TryGetHrefDetails(string htmlTd, out string link, out string name)
{
    var matches = hrefRegex.Match(htmlTd);
    if (matches.Success)
    {
        link = matches.Result("${link}");
        name = matches.Result("${name}");
        return true;
    }
    else
    {
        link = null;
        name = null;
        return false;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.