Як отримати доступ до названих груп захоплення в .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
Зворотний зв'язок повинен бути у форматі (? <Посилання>. *), А не (? <Посилання>. *?)
ТАК Користувач

11
FYI: Якщо ви намагаєтесь зберегти іменовану групу захоплення всередині файлу xml, цей файл <>розірве її. Ви можете використовувати (?'link'.*)замість цього в цьому випадку. Це питання не зовсім відповідне, але я приїхав сюди з пошуку Google ".net з назвою груп захоплення", тому я впевнений, що інші люди також ...
rtpHarry

1
StackOverflow посилання з хорошим , наприклад: stackoverflow.com/a/1381163/463206 Крім того , @rtpHarry, Н.О. <>не порушувати його. Мені вдалося використовувати myRegex.GetGroupNames()колекцію як імена елементів XML.
radarbob

Відповіді:


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}щоб зробити речі ще простішими.
Магнус Сміт

Це повністю відповідає на питання, але має деякі проблеми, які тут занадто довго пояснюються тут, але я пояснив і виправив їх у своїй відповіді нижче
Mariano Desanze

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.