如何访问.NET正则表达式中的命名捕获组?


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用户

11
仅供参考:如果您试图将命名的捕获组存储在xml文件中,则<>它将破坏它。(?'link'.*)在这种情况下,您可以改用。与这个问题并不完全相关,但我是从Google搜索“ .net命名捕获组”中获得的,因此我确定其他人也是如此...
rtpHarry

1
带有很好示例的StackOverflow链接:stackoverflow.com/a/1381163/463206 另外,@ rtpHarry,不,<>不会破坏它。我能够将myRegex.GetGroupNames()集合用作XML元素名称。
Radarbob 2012年

Answers:


263

使用Match对象的组集合,并使用捕获组名对其进行索引,例如

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

10
不要使用var m,因为那将会是一个object
Thomas Weller

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字符串是否匹配模式。如果匹配,则out参数分别包含链接和名称。

/// <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}使事情变得更简单。
Magnus Smith,

这完全回答了问题,但是这里有一些问题太长了,无法解释,但是我在下面的回答中
Mariano Desanze

1

此外,如果某人有一个用例,在对Regex对象执行搜索之前需要组名,则可以使用:

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

1

该答案在Rashmi Pandit的答案的基础上有所改进,它在某种程度上比其他答案更好,因为它似乎可以完全解决问题中详述的确切问题。

坏的部分是效率低下,不能始终使用IgnoreCase选项。

效率低下的部分是因为正则表达式的构造和执行可能会很昂贵,并且在这个答案中它可能只被构造了一次(调用Regex.IsMatch只是在幕后再次构造正则表达式)。和Match方法可以被调用一次,并存储在一个变量,然后linkname应调用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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.