Regex tester · Guide
Regex Balancing Groups: Matching Nested Structures in .NET
Regular expressions provably cannot count, so matching balanced nesting is outside what regex is supposed to do. .NET does it anyway with balancing groups — a capture stack bolted onto the engine.
The syntax
Three constructs work together: (?<name>...) pushes onto a stack, (?<-name>...) pops, and (?(name)fail) checks whether anything is left:
^[^()]*(?:(?<open>\()[^()]*|(?<-open>\))[^()]*)*(?(open)(?!))$Reading it: consume non-parens; then repeatedly either push on ( or pop on ); at the end, (?(open)(?!)) fails if the stack is non-empty. A ) with an empty stack fails the pop, so unbalanced input is rejected in both directions.
Extracting the content
A named group between the push and pop captures what was inside, so you can pull out the balanced section rather than just validating it:
var re = new Regex(@"\((?<content>(?:[^()]|(?<open>\()|(?<-open>\)))*(?(open)(?!)))\)");
var m = re.Match("f(a(b)c)d");
m.Groups["content"].Value; // "a(b)c"What other engines offer
PCRE and Perl have recursion — \((?:[^()]|(?R))*\) — which is shorter and also unreadable. The regex module in Python supports it; the standard re module does not.
JavaScript, Java and Go have nothing equivalent. A pattern using balancing groups is .NET-only and will not survive a port.
When to stop
These patterns are write-only. Six months later nobody, including the author, can safely modify one, and a small requirement change means rewriting from scratch.
A counter loop over the characters is a few lines, runs in linear time, handles error reporting, and any colleague can read it. Use balancing groups when the pattern must live in a configuration value that only accepts a regex — that is genuinely the case they earn their keep in.
Frequently asked questions
Do balancing groups work outside .NET?
No. PCRE and Perl offer recursion instead; most other engines have neither.
Are they slow?
They backtrack heavily and can degrade badly on malformed input. Set a Regex timeout if the input is untrusted.
Should I use one?
Only when a regex is the only thing the surrounding system accepts. Otherwise write a counting loop or a parser.
Ready to try it?
Open the free browser-based Regex tester and apply what you just read — no sign-up, runs locally.
Open the Regex tester tool