tags:

views:

126

answers:

5

I want to write a regular expression which matches anything between () (()) (()()) ((())) ()()().....

+4  A: 

That language is irregular, and so there is no way to write a universal regex that can match it.

Ignacio Vazquez-Abrams
+1: More specifically, it is a _Context Free Language_
Callum Rogers
This answer is wrong.
tchrist
+2  A: 

Such nested structure cannot be effectively handled by regular expressions. What you need is a grammar and a parser for that grammar. In your case the grammar is simple enough. If you are using python try pyparsing or funcparserlib.

With pyparsing you can do the following:

from pyparsing import nestedExpr
nestedExpr().parseString( "(some (string you) (want) (to) test)" ).asList()

This will return a list containing the parsed components of the nested string. The default delimiter for nestedExpr is parenthesis, so you do not have to do anything extra. If you want to use funcpasrerlib you can try the following

from funcparserlib.parser import forward_decl, many, a
bracketed = forward_decl()
bracketed.define(a('(') + many(bracketed) + a(')'))

After this you can call

bracketed.parse( "( (some) ((test) (string) (you) (want)) (to test))" )

and it will return the parsed elements in a tuple.

srean
This is another wrong answer. Just because Python is incapable of doing the sophisticated pattern matching necessary does not mean all other languages are similarly hampered. Perl, PHP, and PCRE can all handle this perfectly well. See my answer.
tchrist
When you add backreference, the possessive "+" or the (?number) syntax it is not a regular language/expression anymore. It can have similar syntax but that doesnt mean its the same thing. Perl 5.10 and above and if I remember correctly, languages in the.Net framework offer such extensions. I think there is a bug in your pattern though I might be wrong, commenting separately there. Oh I cannot comment there. Anyways, I think the (?0) you have should be (?1) because you should recurse once you match at least one matched parenthesis.
srean
No, recursing on (?0) for the whole parenthesized is correct. There is no group 1 to recurse on as written. I *did* test all these. Honest! I prefer the final version with named groups used as a sort of regex subroutines. It's infinitely easier to read, debug, extend, and maintain. Hope this helps.
tchrist
You are probably right about (?0). But if you prefer readability, maintainability, clarity etc nestedExpr().parseString( ) seems shorter and easier :), the right language or not.
srean
+2  A: 

In case you are stuck with language whose regular expression syntax does not support recursive matching I'm giving you my simple Javascript implementation from which you should be able to make your own in the language of your choice:

function testBraces(s) {
    for (var i=0, j=0; i<s.length && j>=0; i++)
        switch(s.charAt(i)) {
            case '(': { j++ ; break; }
            case ')': { j-- ; break; }
        }

    return j == 0;
}

And here you can play with it: http://jsfiddle.net/BFsn2/

Marko Dumic
True, `j` should never go below zero as it indicates imbalance.
Marko Dumic
Tim Pietzcker
@Tim: It was there from the start, as is in the demo (on jsFiddle).
Marko Dumic
OK, obviously I can't read :)
Tim Pietzcker
This answer is also wrong: patterns in modern programming languages are perfectly up to the job.
tchrist
+7  A: 

All these answers claiming you can't use patterns to match a string with balanced nested parens are quite wrong. It's not practical to pretend that the patterns matched by modern programming languages are restricted to "regular languages" in the pathological textbook sense. As soon as you permit backreferences, they're not. This allows real-world patterns to match much more than the textbook versions, making them far more practical.

The simplest pattern for matching balanced parens is \((?:[^()]*+|(?0))*\). But you should never write that, because it is too compact to be easily read. You should always write it with /x mode to allow for whitespace and comments. So write it like this:

m{
  \(              # literal open paren
     (?:          # begin alternation group
         [^()]*+  #  match nonparens possessively
       |          # or else
         (?0)     #  recursively match entire pattern
     )*           # repeat alternation group
  \)              # literal close paren
}x

There's also a lot to be said for naming your abstractions, and decoupling their definition and its ordering from their execution. That leads to this sort of thing:

my $nested_paren_rx = qr{

    (?&nested_parens)

    (?(DEFINE)

        (?<open>       \(       )
        (?<close>       \)      )
        (?<nonparens> [^()]     )

        (?<nested_parens>
            (?&open)
            (?:
                (?&nonparens) *+
              |
                (?&nested_parens)
            ) *
            (?&close)
        )

    )
}x;

The second form is now amenable to inclusion in larger patterns.

Don't ever let anybody tell you can't use a pattern to match something that's recursively defined. As I've just demonstrated, you most certainly can.

While you're at it, make sure never to write line-noise patterns. You don't have to, and you shouldn't. No programming language can be maintainable that forbids white space, comments, subroutines, or alphanumeric identifiers. So use all those things in your patterns.

Of course, it does help to pick the right language for this kind of work. ☺

tchrist
@tchrist: It would be good if you specified what languages your example would work in. Of course, it would also be good if the OP specified what languages he is looking to implement this in.
Avi
@avi: Agreed; hence my last line. As far as I know, recursive patterns work in Perl, PHP, and PCRE. The variable syntax I used was for Perl, but that's somewhat incidental to the problem. I expect that more languages will adopt recursive patterns in the next few years, now that PCRE supports them. Be careful, though, because PCRE has more restrictions on head-vs-tail recursion than Perl does.
tchrist