Skip to content
Mark Whitaker edited this page Jan 6, 2021 · 2 revisions

Contents

Overview

Groups are used in a regex for one (or both) of two purposes:

  1. To group a number of elements together so a quantifier can be applied to the whole group.
  2. To "remember" part of the text matched by the regex so we can extract it later using the Groups and Captures properties of the System.Text.RegularExpressions.Match class.

Grouping elements for quantifiers

Grouping for quantifiers is pretty self-explanatory. A quantifier passed to the Group(), NamedGroup() or NonCapturingGroup() methods will apply to the whole group. For example, this is a very simple regex to match a normal sentence:

var regex = new RegexBuilder()
    .Group(r => r
        .Letter(RegexQuantifier.OneOrMore)
        .Whitespace(),
        RegexQuantifier.OneOrMore
    )
    .Letter(RegexQuantifier.OneOrMore)
    .Text(".")
    .BuildRegex();

Remembering parts of the match

Say we want to match a person's name (two consecutive words each beginning with a capital letter) and then greet them by their first name, we could build a regex like this:

var regex = new RegexBuilder()
    .WordBoundary()
    .Group(r => r
        .UppercaseLetter()
        .LowercaseLetter(RegexQuantifier.OneOrMore)
    )
    .EndGroup()
    .Whitespace()
    .UppercaseLetter()
    .LowercaseLetter(RegexQuantifier.OneOrMore)
    .WordBoundary()
    .BuildRegex();

We can then extract the first name from a successful match like this:

var match = regex.Match(inputString)
if (match.Success)
{
    var firstName = match.Groups[1].Value;
}

Note that Groups is indexed from 1, not 0. For reasons documented elsewhere, Groups[0] will return the whole matched string.

Named groups

If you prefer to avoid numerical indices altogether you can also define named groups which are then indexed by name. Using named groups, our code would look like this:

var regex = new RegexBuilder()
    .WordBoundary()
    .NamedGroup("firstName", r => r
        .UppercaseLetter()
        .LowercaseLetter(RegexQuantifier.OneOrMore)
    )
    .EndGroup()
    .Whitespace()
    .UppercaseLetter()
    .LowercaseLetter(RegexQuantifier.OneOrMore)
    .WordBoundary()
    .BuildRegex();

var match = regex.Match(inputString)
if (match.Success)
{
    var firstName = match.Groups["firstName"];
}

Nesting groups

As with raw regexes, RegexBuilder allows you to nest groups to arbitrary depth. If you use capturing groups, Match.Groups[1] will refer to the first started group, and so on. For example:

var regex = new RegexBuilder()
    .WordBoundary()
    .Group(r1 => r1                         // start of group 1
        .Group(r2 => r2                     // start of group 2
            .UppercaseLetter()
        )                                   // end of group 2
        .LowercaseLetter(RegexQuantifier.OneOrMore)
    )                                       // end of group 1
    .WordBoundary()
    .BuildRegex();

var match = regex.Match("sorry Dave, I can't do that");
if (match.Success)
{
    var name = match.Groups[1].Value;     // "Dave"
    var initial = match.Groups[2].Value;  // "D"
}

Methods

Method Description Raw regex equivalent
Group() Add a group which can be extracted later by indexing into the Match.Groups property with an integer. (...)
NamedGroup() Add a group which can be extracted later by indexing into the Match.Groups property with a string. (?<name>...)
NonCapturingGroup() Add a group which cannot be extracted later from the Match.Groups property. This can be useful if you have more than one group in a regex, and you don't want to a group that's purely for quantifiers to disrupt the indices of your capturing groups. (?:...)

NuGet Version and Downloads count

Clone this wiki locally