Bagaimana cara mengakses grup penangkap bernama di .NET Regex?

255

Saya mengalami kesulitan menemukan sumber daya yang bagus yang menjelaskan cara menggunakan Grup Pengambilan Nama di C #. Ini adalah kode yang saya miliki sejauh ini:

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());

Namun ini selalu menunjukkan baris penuh:

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

Saya telah bereksperimen dengan beberapa "metode" lain yang saya temukan di berbagai situs web tetapi saya tetap mendapatkan hasil yang sama.

Bagaimana saya bisa mengakses grup penangkap bernama yang ditentukan dalam regex saya?

UnkwnTech
sumber
3
Backreference harus dalam format (? <link>. *) Dan tidak (? <link>. *?)
SO Pengguna
11
FYI: Jika Anda mencoba untuk menyimpan grup penangkap bernama di dalam file xml maka <>akan memecahnya. Anda dapat menggunakannya (?'link'.*)sebagai gantinya dalam hal ini. Tidak sepenuhnya relevan dengan pertanyaan ini, tetapi saya mendarat di sini dari pencarian Google ".net named capture groups" jadi saya yakin orang lain juga ...
rtpHarry
1
Tautan StackOverflow dengan contoh yang bagus: stackoverflow.com/a/1381163/463206 Juga, @rtpHarry, Tidak, <>itu tidak akan merusaknya . Saya bisa menggunakan myRegex.GetGroupNames()koleksi sebagai nama elemen XML.
radarbob

Jawaban:

263

Gunakan koleksi grup objek Match, mengindeksnya dengan nama grup capturing, mis

foreach (Match m in mc){
    MessageBox.Show(m.Groups["link"].Value);
}
Paolo Tedesco
sumber
10
Jangan gunakan var m, karena itu akan menjadi object.
Thomas Weller
111

Anda menentukan string grup penangkap bernama dengan meneruskannya ke pengindeks Groupsproperti dari Matchobjek yang dihasilkan .

Ini adalah contoh kecil:

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);
        }
    }
}
Andrew Hare
sumber
10

Contoh kode berikut, akan cocok dengan pola bahkan dalam kasus karakter spasi di antaranya. yaitu:

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

sebaik:

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

Metode mengembalikan benar atau salah, tergantung pada apakah string htmlTd input cocok dengan pola atau tidak. Jika cocok, params masing-masing berisi tautan dan nama.

/// <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;
}

Saya telah menguji ini dan berfungsi dengan benar.

JADI Pengguna
sumber
1
Terima kasih telah mengingatkan saya bahwa kurung kurawal dapat mengakses grup. Saya lebih suka tetap berpegang pada ${1}hal-hal yang lebih sederhana.
Magnus Smith
Ini sepenuhnya menjawab pertanyaan, tetapi memiliki beberapa masalah yang terlalu panjang untuk dijelaskan di sini, tetapi saya menjelaskan dan mengoreksi jawaban saya
Mariano Desanze
1

Selain itu jika seseorang memiliki use case di mana ia membutuhkan nama grup sebelum melakukan pencarian pada objek Regex yang dapat ia gunakan:

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

Jawaban ini meningkat pada jawaban Rashmi Pandit , yang jauh lebih baik daripada yang lain karena tampaknya menyelesaikan masalah yang tepat yang terinci dalam pertanyaan.

Bagian buruknya adalah tidak efisien dan tidak menggunakan opsi IgnoreCase secara konsisten.

Bagian yang tidak efisien adalah karena regex bisa mahal untuk dibangun dan dieksekusi, dan dalam jawaban itu bisa dibangun hanya sekali (pemanggilan Regex.IsMatchhanya membangun regex lagi di belakang layar). Dan Matchmetode bisa dipanggil hanya sekali dan disimpan dalam variabel dan kemudian linkdan nameharus memanggilResult dari variabel itu.

Dan opsi IgnoreCase hanya digunakan di Matchbagian tetapi tidak di Regex.IsMatchbagian.

Saya juga memindahkan definisi Regex di luar metode untuk membangunnya sekali saja (saya pikir adalah pendekatan yang masuk akal jika kita menyimpan bahwa perakitan dengan RegexOptions.Compiledopsi).

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;
    }
}
Mariano Desanze
sumber