Matt
Matt

Reputation: 26971

Get named group subpattern from .NET regex object

Let's say I have the following regex:

var r = new Regex("Space(?<entry>[0-9]{1,3})");

Then I have the string:

"Space123"

Here is my program:

void Main()
{
    Regex r = new Regex("Space(?<entry>[0-9]{1,3})", RegexOptions.ExplicitCapture);
    foreach (Match m in r.Matches("Space123")){
        m.Groups["entry"].Dump(); //Dump() is linqpad to echo the object to console
    }
}

What I want to know is if there is any way to to get the regular expression part that matched? In this case:

(?<entry>[0-9]{1,3})

I can't find it anywhere in the object, but one would think it would be accessible.

Upvotes: 5

Views: 370

Answers (1)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626709

You can leverage the Regex.ToString() method that stores the regular expression pattern. Named capure groups and their respective indices can be obtained using Regex.GetGroupNames() and Regex.GetGroupNumbers().

Also, we need an array/list of the capture groups inside the regex pattern, that is why I am adding rxPairedRoundBrackets regex to capture all texts inside unescaped round brackets.

I suggest this code to get the regex subpattern for a specific named group (edit: now, even handling nested unescaped parenthetical groups!):

var rxPairedRoundBrackets = new Regex(@"(?sx)(?=((?<=[^\\]|^)\(
        (?>
          (?! (?<!\\)\( | (?<!\\)\) ) .
          |
          (?<!\\)\( (?<Depth>)
          |
          (?<!\\)\) (?<-Depth>)
        )*
        (?(Depth)(?!))
        (?<!\\)\)))+");
var r = new Regex(@"(?<OuterSpace>Spa(?<ce>ce))(?<entry>\([0-9]{1,3}\))", RegexOptions.ExplicitCapture);
var bracketedGrps = rxPairedRoundBrackets.Matches(r.ToString()).Cast<Match>().Select(p => p.Groups[1].Value);
var GroupDict = r.GetGroupNames().Zip(r.GetGroupNumbers(), (s, i) => new { s, i })
                                 .ToDictionary(item => item.s, item => item.i);
foreach (Match m in r.Matches("My New Space(123)"))
{
    var id = "entry";
    var grp = m.Groups[id]; // Just to see the group value
    var groupThatMatched = bracketedGrps.ElementAt(GroupDict[id] - 1);
}

And here is the code for your case:

r = new Regex("Space(?<entry>[0-9]{1,3})", RegexOptions.ExplicitCapture);
bracketedGrps = rxPairedRoundBrackets.Matches(r.ToString()).Cast<Match>().Select(p => p.Groups[1].Value);
GroupDict = r.GetGroupNames().Zip(r.GetGroupNumbers(), (s, i) => new { s, i })
                             .ToDictionary(item => item.s, item => item.i);
foreach (Match m in r.Matches("Space123"))
{
   var id = "entry";
   var grp = m.Groups[id];
   var groupThatMatched = bracketedGrps.ElementAt(GroupDict[id] - 1);
}

Output:

enter image description here

Upvotes: 5

Related Questions