111

Is there a way to get the name of a captured group in C#?

string line = "No.123456789  04/09/2009  999";
Regex regex = new Regex(@"(?<number>[\d]{9})  (?<date>[\d]{2}/[\d]{2}/[\d]{4})  (?<code>.*)");

GroupCollection groups = regex.Match(line).Groups;

foreach (Group group in groups)
{
    Console.WriteLine("Group: {0}, Value: {1}", ???, group.Value);
}

I want to get this result:

Group: [I don´t know what should go here], Value: 123456789  04/09/2009  999
Group: number, Value: 123456789
Group: date,   Value: 04/09/2009
Group: code,   Value: 999
Liam
  • 27,717
  • 28
  • 128
  • 190
Luiz Damim
  • 3,803
  • 2
  • 27
  • 31

6 Answers6

141

Use GetGroupNames to get the list of groups in an expression and then iterate over those, using the names as keys into the groups collection.

For example,

GroupCollection groups = regex.Match(line).Groups;

foreach (string groupName in regex.GetGroupNames())
{
    Console.WriteLine(
       "Group: {0}, Value: {1}",
       groupName,
       groups[groupName].Value);
}
Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
28

The cleanest way to do this is by using this extension method:

public static class MyExtensionMethods
{
    public static Dictionary<string, string> MatchNamedCaptures(this Regex regex, string input)
    {
        var namedCaptureDictionary = new Dictionary<string, string>();
        GroupCollection groups = regex.Match(input).Groups;
        string [] groupNames = regex.GetGroupNames();
        foreach (string groupName in groupNames)
            if (groups[groupName].Captures.Count > 0)
                namedCaptureDictionary.Add(groupName,groups[groupName].Value);
        return namedCaptureDictionary;
    }
}


Once this extension method is in place, you can get names and values like this:

    var regex = new Regex(@"(?<year>[\d]+)\|(?<month>[\d]+)\|(?<day>[\d]+)");
    var namedCaptures = regex.MatchNamedCaptures(wikiDate);

    string s = "";
    foreach (var item in namedCaptures)
    {
        s += item.Key + ": " + item.Value + "\r\n";
    }

    s += namedCaptures["year"];
    s += namedCaptures["month"];
    s += namedCaptures["day"];
whitneyland
  • 10,632
  • 9
  • 60
  • 68
14

Since .NET 4.7, there is Group.Name property available.

Jozef Benikovský
  • 1,121
  • 10
  • 9
  • In .net Core at-least it's "Helpfully" set to 0,1,2 etc to indicate the number of the group, like that's going to be useful. It's amazing that this is still such a mess after so long! – Kinetic Mar 29 '22 at 11:24
8

You should use GetGroupNames(); and the code will look something like this:

    string line = "No.123456789  04/09/2009  999";
    Regex regex = 
        new Regex(@"(?<number>[\d]{9})  (?<date>[\d]{2}/[\d]{2}/[\d]{4})  (?<code>.*)");

    GroupCollection groups = regex.Match(line).Groups;

    var grpNames = regex.GetGroupNames();

    foreach (var grpName in grpNames)
    {
        Console.WriteLine("Group: {0}, Value: {1}", grpName, groups[grpName].Value);
    }
Eran Betzalel
  • 4,105
  • 3
  • 38
  • 66
2

To update the existing extension method answer by @whitneyland with one that can handle multiple matches:

public static List<Dictionary<string, string>> MatchNamedCaptures(this Regex regex, string input)
    {
        var namedCaptureList = new List<Dictionary<string, string>>();
        var match = regex.Match(input);

        do
        {
            Dictionary<string, string> namedCaptureDictionary = new Dictionary<string, string>();
            GroupCollection groups = match.Groups;

            string[] groupNames = regex.GetGroupNames();
            foreach (string groupName in groupNames)
            {
                if (groups[groupName].Captures.Count > 0)
                    namedCaptureDictionary.Add(groupName, groups[groupName].Value);
            }

            namedCaptureList.Add(namedCaptureDictionary);
            match = match.NextMatch();
        }
        while (match!=null && match.Success);

        return namedCaptureList;
    }

Usage:

  Regex pickoutInfo = new Regex(@"(?<key>[^=;,]+)=(?<val>[^;,]+(,\d+)?)", RegexOptions.ExplicitCapture);

  var matches = pickoutInfo.MatchNamedCaptures(_context.Database.GetConnectionString());

  string server = matches.Single( a => a["key"]=="Server")["val"];
Kinetic
  • 700
  • 8
  • 15
-1

The Regex class is the key to this!

foreach(Group group in match.Groups)
{
    Console.WriteLine("Group: {0}, Value: {1}", regex.GroupNameFromNumber(group.Index), group.Value);
}

http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.groupnamefromnumber.aspx

Joel
  • 47
  • 1
  • 12
    This is incorrect, the `group.Index` is the position of the start of the match group in the original text. Not the "index" of the group in the regex. – squig Feb 09 '15 at 13:08