242

Is it possible to write a regular expression that matches a nested pattern that occurs an unknown number of times? For example, can a regular expression match an opening and closing brace when there are an unknown number of open/close braces nested within the outer braces?

For example:

public MyMethod()
{
  if (test)
  {
    // More { }
  }

  // More { }
} // End

Should match:

{
  if (test)
  {
    // More { }
  }

  // More { }
}
codeforester
  • 39,467
  • 16
  • 112
  • 140
Richard Dorman
  • 23,170
  • 16
  • 45
  • 49
  • 29
    To unambiguously answer this question, one first needs to define the term: "regular expression". – ridgerunner Mar 15 '11 at 02:58
  • 6
    From the books, *regular expressions* can't do that, but *context-free expressions* can. From the tools, modern expression parsers will call `regular expression` something that is using an external stack, meaning able to backtrack, meaning able to recurse: those are `context-free expressions` in practice and as such you can do it as a one-liner with simili-[PCRE2](http://www.pcre.org/) (PHP, Java, .NET, Perl, ...) or [ICU](http://userguide.icu-project.org/strings/regexp)-compliant (Obj-C/Swift) tools, often with the `(?>...)` syntax, or alternatives such as the `(?R)` or `(?0)` syntaxes. – Cœur Aug 05 '15 at 07:38

11 Answers11

281

No. It's that easy. A finite automaton (which is the data structure underlying a regular expression) does not have memory apart from the state it's in, and if you have arbitrarily deep nesting, you need an arbitrarily large automaton, which collides with the notion of a finite automaton.

You can match nested/paired elements up to a fixed depth, where the depth is only limited by your memory, because the automaton gets very large. In practice, however, you should use a push-down automaton, i.e a parser for a context-free grammar, for instance LL (top-down) or LR (bottom-up). You have to take the worse runtime behavior into account: O(n^3) vs. O(n), with n = length(input).

There are many parser generators avialable, for instance ANTLR for Java. Finding an existing grammar for Java (or C) is also not difficult.
For more background: Automata Theory at Wikipedia

Torsten Marek
  • 83,780
  • 21
  • 91
  • 98
  • 64
    Torsten is correct as far as theory is concerned. In practice many implementations have some trick in order to allow you to perform recursive "regular expressions". E.g. see the chapter "Recursive patterns" in http://php.net/manual/en/regexp.reference.php – daremon Sep 25 '08 at 15:26
  • 2
    I am spoiled by my upbringing in Natural Language Processing and the automata theory it included. – Torsten Marek Sep 25 '08 at 15:31
  • 7
    A refreshingly clear answer. Best "why not" I've ever seen. – Ben Doom Sep 25 '08 at 16:35
  • 15
    Regular expressions in language theory and regular expressions in practice are different beasts... since _regular_ expressions can't have niceties such as back references, forward references etc. – Novikov Oct 04 '10 at 16:54
  • *A finite automaton (which is the data structure underlying a regular expression) does not have memory apart from the state it's in, and if you have arbitrarily deep nesting, you need an arbitrarily large automaton, which collides with the notion of a finite automaton.* - best answer on this topic I've seen so far – Rafael Eyng Sep 21 '15 at 00:33
  • 1
    @TorstenMarek - can you confirm this is still true? Other sources state that if a regex engine supports features such as back-references it becomes a class 2 grammar (context-free) rather than a class 3 (regular grammar). Therefore PCRE for example - is capable of handling nested structures. The confusion comes from the fact that 'regex' in the real world are no longer regular in the technical sense. If this is correct it would be great to update this answer. – Andy Baker Aug 13 '16 at 13:25
  • There is a way to accomplish this, but it will not be purely regex. You need to match every instance of braces/brackets/parens (global), then use some programming language to recursively replace/mark the nested matches within the parent. – Grant Eagon Sep 06 '17 at 20:13
  • This answer is way above my head. And then I found a working regex: http://www.drregex.com/2017/11/match-nested-brackets-with-regex-new.html – Aaron Cicali Apr 19 '18 at 19:34
  • Correction... that regex works in *most cases* :( – Aaron Cicali Apr 19 '18 at 20:07
  • Correction to 2008 post in 2021: see https://www.php.net/manual/en/regexp.reference.recursive.php . – David Spector Apr 23 '21 at 17:48
48

Using regular expressions to check for nested patterns is very easy.

'/(\((?>[^()]+|(?1))*\))/'
Michael
  • 11,912
  • 6
  • 49
  • 64
  • 4
    I agree. However,one problem with the `(?>...)` atomic group syntax (under PHP 5.2) is that the `?>` portion is interpreted as: "end-of-script"! Here is how I would write it: `/\((?:[^()]++|(?R))*+\)/`. This is a bit more efficient for both matching and non-matching. In its minimal form, `/\(([^()]|(?R))*\)/`, it is truly a beautiful thing! – ridgerunner Mar 12 '11 at 06:35
  • 1
    Double +? I used `(?1)` to allow for comments to be within other text (I ripped it and simplified it from my email address regular expression). And `(?>` was used because I believe it makes it fail faster (if required). Is that not correct? – Michael Mar 19 '11 at 12:27
  • 17
    Can you add an explanation for each part of the regex? – EricP Jan 15 '15 at 18:01
  • For string `'(a (b c)) (d e)'`, using simple expression `'/\([^()]*\)/'` gives me the same result. Are there benefits to your long expression? – Cœur Oct 13 '15 at 07:38
  • Try using `/^(\((?>[^()]+|(?1))*\))+$/` and `/^\([^()]*\)+$/` to match `(a (b c))(d e)`. The former matches but the latter doesn't. – Michael Oct 13 '15 at 09:16
  • @MichaelRushton your solution worked fine for me. But I'm just wondering what's the difference between `?>` and `?:` ? Tried with both of them and they all seem to work. – elquimista Feb 01 '16 at 15:18
  • It makes it an atomic group, and is used to prevent [catastrophic backtracking](http://www.regular-expressions.info/catastrophic.html). – Michael Feb 01 '16 at 18:58
  • What about more explanation than "It's very easy".. this regex is gibberish – Tofandel Mar 29 '19 at 02:51
  • Finally found someone who cared enough to explain: https://stackoverflow.com/questions/26385984/recursive-pattern-in-regex – Tim May 21 '19 at 21:24
  • Or here: http://rachbelaid.com/recursive-regular-experession/ – Tim May 21 '19 at 21:38
  • Or even better: https://www.rexegg.com/regex-recursion.html – Tim May 21 '19 at 22:19
  • This solution using ?1 works with (*SKIP)(*FAIL) when the ?R method does not. I created an example here: https://regex101.com/r/xkVzVP/1 – Ben Sep 29 '22 at 15:10
33

Probably working Perl solution, if the string is on one line:

my $NesteD ;
$NesteD = qr/ \{( [^{}] | (??{ $NesteD }) )* \} /x ;

if ( $Stringy =~ m/\b( \w+$NesteD )/x ) {
    print "Found: $1\n" ;
  }

HTH

EDIT: check:

And one more thing by Torsten Marek (who had pointed out correctly, that it's not a regex anymore):

Community
  • 1
  • 1
Zsolt Botykai
  • 50,406
  • 14
  • 85
  • 110
  • 9
    Yup. Perl's "regular expressions" aren't (and haven't been for a very long time). It should be noted that recursive regexes are a new feature in Perl 5.10 and that even though you can do this you probably shouldn't in most of the cases that commonly come up (e.g. parsing HTML). – Michael Carman Sep 25 '08 at 15:09
  • 1
    http://perldoc.perl.org/perlretut.html – Brad Gilbert Oct 16 '08 at 16:30
20

The Pumping lemma for regular languages is the reason why you can't do that.

The generated automaton will have a finite number of states, say k, so a string of k+1 opening braces is bound to have a state repeated somewhere (as the automaton processes the characters). The part of the string between the same state can be duplicated infinitely many times and the automaton will not know the difference.

In particular, if it accepts k+1 opening braces followed by k+1 closing braces (which it should) it will also accept the pumped number of opening braces followed by unchanged k+1 closing brases (which it shouldn't).

Rafał Dowgird
  • 43,216
  • 11
  • 77
  • 90
19

Yes, if it is .NET RegEx-engine. .Net engine supports finite state machine supplied with an external stack. see details

itzmebibin
  • 9,199
  • 8
  • 48
  • 62
14

Proper Regular expressions would not be able to do it as you would leave the realm of Regular Languages to land in the Context Free Languages territories.

Nevertheless the "regular expression" packages that many languages offer are strictly more powerful.

For example, Lua regular expressions have the "%b()" recognizer that will match balanced parenthesis. In your case you would use "%b{}"

Another sophisticated tool similar to sed is gema, where you will match balanced curly braces very easily with {#}.

So, depending on the tools you have at your disposal your "regular expression" (in a broader sense) may be able to match nested parenthesis.

Remo.D
  • 16,122
  • 6
  • 43
  • 74
11

YES

...assuming that there is some maximum number of nestings you'd be happy to stop at.

Let me explain.


@torsten-marek is right that a regular expression cannot check for nested patterns like this, BUT it is possible to define a nested regex pattern which will allow you to capture nested structures like this up to some maximum depth. I created one to capture EBNF-style comments (try it out here), like:

(* This is a comment (* this is nested inside (* another level! *) hey *) yo *)

The regex (for single-depth comments) is the following:

m{1} = \(+\*+(?:[^*(]|(?:\*+[^)*])|(?:\(+[^*(]))*\*+\)+

This could easily be adapted for your purposes by replacing the \(+\*+ and \*+\)+ with { and } and replacing everything in between with a simple [^{}]:

p{1} = \{(?:[^{}])*\}

(Here's the link to try that out.)

To nest, just allow this pattern within the block itself:

p{2} = \{(?:(?:p{1})|(?:[^{}]))*\}
  ...or...
p{2} = \{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\}

To find triple-nested blocks, use:

p{3} = \{(?:(?:p{2})|(?:[^{}]))*\}
  ...or...
p{3} = \{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}

A clear pattern has emerged. To find comments nested to a depth of N, simply use the regex:

p{N} = \{(?:(?:p{N-1})|(?:[^{}]))*\}

  where N > 1 and
  p{1} = \{(?:[^{}])*\}

A script could be written to recursively generate these regexes, but that's beyond the scope of what I need this for. (This is left as an exercise for the reader. )

awwsmm
  • 1,353
  • 1
  • 18
  • 28
  • Java version of exercise: `String getNestedBraceRegex(int n) {if(n == 1) return "\\{(?:[^{}])*\\}"; else return "\\{(?:(?:" + getNestedBraceRegex(n-1) + ")|(?:[^{}]))*\\}";}` Perfect, thanks. PCRE/2 is way more elegant: `(\{((?>[^{}]+|(?1))*)\})` but that won't work e.g. in Java. –  Jul 12 '21 at 11:45
5

Using the recursive matching in the PHP regex engine is massively faster than procedural matching of brackets. especially with longer strings.

http://php.net/manual/en/regexp.reference.recursive.php

e.g.

$patt = '!\( (?: (?: (?>[^()]+) | (?R) )* ) \)!x';

preg_match_all( $patt, $str, $m );

vs.

matchBrackets( $str );

function matchBrackets ( $str, $offset = 0 ) {

    $matches = array();

    list( $opener, $closer ) = array( '(', ')' );

    // Return early if there's no match
    if ( false === ( $first_offset = strpos( $str, $opener, $offset ) ) ) {
        return $matches;
    }

    // Step through the string one character at a time storing offsets
    $paren_score = -1;
    $inside_paren = false;
    $match_start = 0;
    $offsets = array();

    for ( $index = $first_offset; $index < strlen( $str ); $index++ ) {
        $char = $str[ $index ];

        if ( $opener === $char ) {
            if ( ! $inside_paren ) {
                $paren_score = 1;
                $match_start = $index;
            }
            else {
                $paren_score++;
            }
            $inside_paren = true;
        }
        elseif ( $closer === $char ) {
            $paren_score--;
        }

        if ( 0 === $paren_score ) {
            $inside_paren = false;
            $paren_score = -1;
            $offsets[] = array( $match_start, $index + 1 );
        }
    }

    while ( $offset = array_shift( $offsets ) ) {

        list( $start, $finish ) = $offset;

        $match = substr( $str, $start, $finish - $start );
        $matches[] = $match;
    }

    return $matches;
}
Pete B
  • 1,709
  • 18
  • 11
3

No, you are getting into the realm of Context Free Grammars at that point.

gvlasov
  • 18,638
  • 21
  • 74
  • 110
Craig H
  • 7,949
  • 16
  • 49
  • 61
3

as zsolt mentioned, some regex engines support recursion -- of course, these are typically the ones that use a backtracking algorithm so it won't be particularly efficient. example: /(?>[^{}]*){(?>[^{}]*)(?R)*(?>[^{}]*)}/sm

0

This seems to work: /(\{(?:\{.*\}|[^\{])*\})/m

Sean Huber
  • 640
  • 4
  • 7