Press "Enter" to skip to content

Recursive Patterns in PHP Regex

I remember a colleague asking me a while ago: can a regex handle matching paired brackets?
For example, for the following string to be matched:

   ((()))

this is a string of paired brackets. Whereas for the following string:

   ((()

it is not a string of paired brackets.
In the past, a regex could not handle this kind of case — at best it could only handle recursion of a fixed depth, and could not handle unbounded recursion... But after perl 5.6, a new feature was introduced: Recursive patterns, which makes this kind of requirement possible to handle correctly.
Recursive patterns introduced a new symbol (?R). This symbol can represent the regex pattern itself. For example:

    #1(?R)*#

Let's look at this regex carefully. First it matches the digit "1". Then (?R)* means the pattern itself, i.e. it can be thought of as:

    #1(the pattern itself (the pattern itself).....)*#

So, for the "paired brackets" case mentioned at the beginning, we can write the following regex:

     #\((?R)*\)#

and it will handle it correctly.
A reminder: when using it, be careful to always give the recursion a terminating condition. For example, if the example above were written as:

   #1(?R)#

it would not work correctly, because when expanded it means matching an unlimited number of "1"s. So in the example above it was written as (?R)*, allowing it to have a terminating condition (it can be zero occurrences).
Also, this new feature also supports numbered references (?index). For example:

     #(1)(2)(3)(?3)(?2)(?1)#

means matching 123321.
If you want to learn more about this feature, see: http://www.php.net/manual/en/regexp.reference.recursive.php
Thanks to windy and shiwei for their help 🙂

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.