Làm cách nào để truy cập các nhóm bắt giữ có tên trong .NET Regex?


255

Tôi đang gặp khó khăn trong việc tìm kiếm một tài nguyên tốt giải thích cách sử dụng Nhóm bắt giữ được đặt tên trong C #. Đây là mã mà tôi có cho đến nay:

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

Tuy nhiên, điều này luôn chỉ hiển thị dòng đầy đủ:

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

Tôi đã thử nghiệm với một số "phương pháp" khác mà tôi đã tìm thấy trên các trang web khác nhau nhưng tôi vẫn nhận được kết quả tương tự.

Làm cách nào tôi có thể truy cập vào các nhóm bắt giữ được đặt tên được chỉ định trong biểu thức chính quy của tôi?


3
Backreference phải ở định dạng (? <Link>. *) Chứ không phải (? <Link>. *?)
Người dùng SO

11
FYI: Nếu bạn đang cố lưu trữ một nhóm chụp có tên trong một tệp xml thì nó <>sẽ phá vỡ nó. Bạn có thể sử dụng (?'link'.*)thay thế trong trường hợp này. Không hoàn toàn liên quan đến câu hỏi này nhưng tôi đã đến đây từ một tìm kiếm của Google về ".net có tên là các nhóm bắt giữ" vì vậy tôi chắc chắn rằng những người khác cũng vậy ...
rtpHarry

1
Liên kết StackOverflow với ví dụ hay: stackoverflow.com/a/1381163/463206 Ngoài ra, @rtpHarry, Không có <>sẽ không phá vỡ nó. Tôi đã có thể sử dụng myRegex.GetGroupNames()bộ sưu tập làm tên các thành phần XML.
radarbob

Câu trả lời:


263

Sử dụng bộ sưu tập nhóm của đối tượng Match, lập chỉ mục nó với tên nhóm bắt giữ, vd

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

10
Đừng sử dụng var m, vì đó sẽ là một object.
Thomas Weller

111

Bạn chỉ định chuỗi nhóm chụp được đặt tên bằng cách chuyển nó tới bộ chỉ mục của thuộc Groupstính của một Matchđối tượng kết quả .

Đây là một ví dụ nhỏ:

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

Mẫu mã sau đây, sẽ khớp với mẫu ngay cả trong trường hợp ký tự khoảng trắng ở giữa. I E :

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

cũng như:

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

Phương thức trả về đúng hay sai, tùy thuộc vào việc chuỗi htmlTd đầu vào có khớp với mẫu hay không. Nếu nó khớp, các tham số out chứa liên kết và tên tương ứng.

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

Tôi đã thử nghiệm điều này và nó hoạt động chính xác.


1
Cảm ơn đã nhắc nhở tôi rằng niềng răng xoăn có thể truy cập vào các nhóm. Tôi thích gắn bó ${1}để giữ cho mọi thứ thậm chí đơn giản hơn.
Magnus Smith

Điều này hoàn toàn trả lời câu hỏi, nhưng có một số vấn đề quá dài để giải thích ở đây, nhưng tôi đã giải thích và sửa những câu hỏi
Mariano Desanze

1

Ngoài ra, nếu ai đó có trường hợp sử dụng mà anh ta cần tên nhóm trước khi thực hiện tìm kiếm trên đối tượng Regex, anh ta có thể sử dụng:

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

1

Câu trả lời này cải thiện câu trả lời của Rashmi Pandit , theo cách tốt hơn phần còn lại bởi vì nó dường như giải quyết hoàn toàn vấn đề chính xác được nêu chi tiết trong câu hỏi.

Phần xấu là không hiệu quả và không sử dụng tùy chọn IgnoreCase một cách nhất quán.

Phần không hiệu quả là do regex có thể tốn kém để xây dựng và thực thi, và trong câu trả lời đó, nó có thể đã được xây dựng chỉ một lần (gọi Regex.IsMatchchỉ là xây dựng regex một lần nữa đằng sau hiện trường). Và Matchphương thức có thể được gọi chỉ một lần và được lưu trữ trong một biến và sau đó linknamesẽ gọi Resulttừ biến đó.

Và tùy chọn IgnoreCase chỉ được sử dụng trong Matchphần chứ không phải trong Regex.IsMatchphần.

Tôi cũng đã di chuyển định nghĩa Regex bên ngoài phương thức để xây dựng nó chỉ một lần (tôi nghĩ là cách tiếp cận hợp lý nếu chúng ta lưu trữ tập hợp đó với RegexOptions.Compiledtùy chọn).

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.