Как получить доступ к именованным группам захвата в .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> 

Я экспериментировал с несколькими другими «методами», которые я нашел на различных сайтах, но я продолжаю получать тот же результат.

Как я могу получить доступ к именованным группам захвата, которые указаны в моем регулярном выражении?


3
Обратные ссылки должны быть в формате (? <Link>. *), А не (? <Link>. *?)
SO User

11
К вашему сведению: если вы пытаетесь сохранить именованную группу захвата в XML-файле, то <>она сломается. Вы можете использовать (?'link'.*)вместо этого в этом случае. Не совсем уместно в этом вопросе, но я попал сюда из поиска в Google «.net именованных групп захвата», так что я уверен, что другие люди тоже ...
rtpHarry

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.
Томас Веллер

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>

Метод возвращает true или false, в зависимости от того, соответствует ли введенная строка htmlTd шаблону или нет. Если это соответствует, выходные параметры содержат ссылку и имя соответственно.

/// <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}чтобы держать вещи еще проще.
Магнус Смит

Это полностью отвечает на вопрос, но есть некоторые проблемы, которые здесь слишком долго объяснять, но я объяснил и исправил их в своем ответе ниже
Мариано Дезанце

1

Кроме того, если у кого-то есть сценарий использования, где ему нужны имена групп перед выполнением поиска по объекту Regex, он может использовать:

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

1

Этот ответ улучшает ответ Рашми Пандита , который несколько лучше, чем остальные, потому что кажется, что он полностью решает точную проблему, подробно описанную в вопросе.

Плохая часть заключается в том, что он неэффективен и не использует опцию IgnoreCase последовательно.

Неэффективная часть заключается в том, что регулярное выражение может быть дорогим для создания и выполнения, и в этом ответе оно могло быть Regex.IsMatchсоздано только один раз (вызов просто создавал регулярное выражение снова за сценой). И Matchметод мог быть вызван только один раз и сохранен в переменной, а затем linkи nameдолжен вызываться 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;
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.