.NET Regex'te adlandırılmış yakalama gruplarına nasıl erişirim?


255

C # Adlandırılmış Yakalama Grupları kullanmayı açıklayan iyi bir kaynak bulmakta zorlanıyorum. Bu şimdiye kadar sahip kod:

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

Ancak bu her zaman tam çizgiyi gösterir:

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

Çeşitli web sitelerinde bulduğum birkaç başka "yöntem" denedim ancak aynı sonucu almaya devam ediyorum.

Normal ifademde belirtilen adlandırılmış yakalama gruplarına nasıl erişebilirim?


3
Geri başvuru biçimi (? <link>. *) Biçiminde olmalı ve (? <link>. *?) Biçiminde olmamalıdır
SO Kullanıcı

11
Bilginize: Adlandırılmış bir yakalama grubunu bir xml dosyasının içinde saklamaya çalışıyorsanız, o dosya <>onu bozacaktır. Bunun (?'link'.*)yerine bu durumda kullanabilirsiniz . Bu soru ile tamamen alakalı değil, ancak burada ".net adlı yakalama grupları" adlı bir Google aramasından indi, bu yüzden eminim diğer insanlar da ...
rtpHarry

1
Güzel örnekli StackOverflow bağlantısı: stackoverflow.com/a/1381163/463206 Ayrıca, @rtpHarry, Hayır, <>onu kırmayacak. myRegex.GetGroupNames()Koleksiyonu XML öğesi adları olarak kullanabildim .
radarbob

Yanıtlar:


263

Eşleştirme nesnesinin grup koleksiyonunu kullanarak, yakalama grubu adıyla dizine ekleyin, ör.

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

10
Kullanmayın var m, çünkü bu bir olur object.
Thomas Weller

111

Adlandırılmış yakalama grubu dizesini, Groupssonuçta elde edilen bir Matchnesnenin özelliğinin dizinleyicisine ileterek belirtirsiniz .

İşte küçük bir örnek:

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

Aşağıdaki kod örneği, aradaki boşluk karakterleri durumunda bile kalıpla eşleşecektir. yani:

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

Hem de:

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

Yöntem, giriş htmlTd dizesinin kalıpla eşleşip eşleşmemesine bağlı olarak true veya false değerini döndürür. Eşleşirse, çıkış parametreleri sırasıyla bağlantı ve adı içerir.

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

Bunu test ettim ve düzgün çalışıyor.


1
Kıvırcık parantezlerin gruplara erişebileceğini hatırlattığınız için teşekkürler. ${1}İşleri daha da basit tutmak için sadık kalmayı tercih ederim .
Magnus Smith

Bu, soruyu tamamen cevaplıyor, ancak burada açıklamak için çok uzun bazı sorunları var, ancak aşağıdaki
cevabımdaki

1

Ek olarak, birinin Regex nesnesinde arama yapmadan önce grup adlarına ihtiyaç duyduğu bir kullanım durumu varsa şunları kullanabilir:

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

1

Bu cevaplar Rashmi Pandit'in cevabında iyileşiyor , ki bu bir şekilde diğerlerinden daha iyi, çünkü soruda ayrıntıları verilen problemi tamamen çözüyor gibi görünüyor.

Kötü yanı, verimsiz olması ve IgnoreCase seçeneğini sürekli kullanmamasıdır.

Verimsiz kısım, regex'in inşa edilmesi ve yürütülmesi için pahalı olabileceğidir ve bu cevapta sadece bir kez inşa edilebilirdi (çağrı Regex.IsMatch, regex'i sahnenin arkasında tekrar inşa ediyordu). Ve Matchyöntemi yalnızca bir kez aradı ve bir değişkene saklanır ve daha sonra olabilirdi linkve nameçağırmalıdır Resulto değişkenden.

Ve IgnoreCase seçeneği sadece Matchparçada kullanıldı, parçada kullanılmadı Regex.IsMatch.

Ayrıca Regex tanımını sadece bir kez inşa etmek için yöntemin dışına taşıdım (montajı bu RegexOptions.Compiledseçenekle birlikte saklıyorsak mantıklı bir yaklaşım olduğunu düşünüyorum ).

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;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.