Named and Non-Capturing Groups in .NET Regular Expressions

The previous article introduced the .NET Group and GroupCollection classes, explained their uses and place in the .NET regular expression class hierarchy, and gave an example of how to define and use groups to extract or isolate sub-matches of a regular expression match. This article covers two more facets of using groups: creating named groups and defining non-capturing groups.

Named Groups

As a refresher, the following is an example of a simple regular expression pattern for North American phone numbers. The parentheses define the group, isolating the area code of the phone number:

(\d{3})-\d{3}-\d{4}

The following snippet shows how to instantiate a Regex object using this pattern, how to enumerate the resulting matches, and then how to extract the specific group from each match's Groups member (The Match::Groups member is a collection of type GroupCollection that contains all the Group objects related to the match.):

This works, but the index value into the Groups object is hard-coded, which is a problem. (The value I used is 1 because the entire match is the first entry in the Groups collection and referenced at 0.) As a result, if at a later date, you or someone else disturbs the pattern such that the second group in the Groups object is not the area code, the code depending on this order will fail. This tip illustrates how to name your groups such as to isolate them from changes to the pattern.

The syntax for naming a group, while not very intuitive, is easy and looks like this:

(?<GROUPNAME>expression)

Therefore, in order to name the area code group, you would simply alter the pattern as follows:

(?<AreaCode>\\d{3})-\\d{3}-\\d{4}

Now that the group is named, you just need to know how to extract it. The previous code snippet used the Groups object's Item indexer property to extract the desired group by index value. This is because the Groups object is an instance of the GroupCollection class and, like almost all collection classes, the Item property takes an numeric parameter that represents the desired object's place in the collection: 0 for the first object, 1 for the second object, and so on.

In the case of the GroupCollection::Item property, it is overloaded to also accept a value of type String that represents the actual name given to the group. Therefore, using the example where I gave the name of AreaCode to the group, I can now extract that value using either of these forms:

match->Groups->Item[1]->Value
match->Groups->Item["AreaCode"]->Value

The second (named group) form is preferred and strongly recommended as it better isolates the code from the pattern changing.