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:


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.):

String* input   = S"test: 404-555-1212 and 770-555-1212";
String* pattern = S"(\\d{3})-\\d{3}-\\d{4}";

Regex* rex = new Regex(pattern);

// for all the matches
for (Match* match = rex->Match(input);
      match = match->NextMatch())

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:


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


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:


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

Named and Non-Capturing Groups in .NET Regular Expressions

Non-Capturing Groups

Groups are not always defined in order to create sub-matches. Sometimes, groups get created as a side effect of the parenthetical syntax used to isolate a part of the expression such that modifiers, operators, or quantifiers can act on the isolated part of the expression. Irregardless of your reason for isolating a part of the pattern, once you do it using the parenthesis symbols, the regular expressions parser creates Group objects for that group within each Match object's group collection (Groups).

An example might better explain what I mean. Say you have a pattern to search a string for occurrences of the words "on", "an", and "in":


If you tested this pattern with the following function, which simply displays all the groups of each match, you'd find that each match results in five groups:

void DisplayGroups(String* input, String* pattern)
    StringBuilder* results = new StringBuilder();

    Regex* rex = new Regex(pattern);

    // for all the matches
    for (Match* match = rex->Match(input);
         match = match->NextMatch())
      results->AppendFormat(S"Match {0} at {1}\r\n",

      // for all of THIS match's groups
      GroupCollection* groups = match->Groups;
      for (int i = 0; i < groups->Count; i++)
        results->AppendFormat(S"\tGroup {0} at {1}\r\n",

  catch(Exception* pe)

Figure 1 shows the results of running the following code using the DisplayGroups function:

// Example usage of the DisplayGroups function
DisplayGroups(S"Tommy sat on a chair, in a room",


Figure 1: Using Parentheses in Patterns Always Creates Groups

Therefore, for efficiency—especially if you're processing huge amounts of data with your regular expressions, define the group as "non-capturing" by giving your group a blank name as follows:


If you run this pattern through the DisplayGroups function, you'll see that the only groups created represent the entire match (see Figure 2). (There is no way to eliminate that group.)


Figure 2: The Only Groups Created Represent the Entire Match

Looking Ahead

The previous two articles covered groups and group collections in regular expressions. However, one area that is closely tied to groups that I haven't touched on yet is captures. Therefore, the upcoming articles will explain what captures are and how they related to matches and groups.

About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The open source cloud computing project OpenStack has come a long way since NASA and Rackspace launched it in 2010. Backed by leading technology infrastructure providers including Cisco, Dell, EMC, HP, IBM, Intel, and VMware, OpenStack underpins significant workloads at an increasingly diverse set of organizations, including BWM, CERN, Comcast, eBay, and Wal-Mart. For CIOs engaged in broader programs to win, serve, and retain customers -- and refocus business technology (BT) spend -- a planned and pragmatic …

  • Protect Your Business against Today's Rapidly Growing Threats Your business, employees, partners, and customers are depending more and more on network connectivity, and protecting data and infrastructure has moved to the top of the IT agenda. You can't arm yourself against today's rapidly increasing threats unless you understand how they work. Read this ebook to learn more about the top 10 DNS attacks that can target your external and internal DNS infrastructure, the impact they can have on the DNS server …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date