C.W.K.
Stream
Lesson 03 of 12 · published

Recursive Patterns — (?R)

~10 min · recursive, advanced, pcre

Level 0Pattern-Curious
0 XP0/90 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

Patterns that match themselves

Recursion lets a pattern reference itself. The classic use: matching balanced brackets to arbitrary depth, which pure regex (a regular language) can't do. With recursion, regex extends into context-free grammar territory.

Syntax

  • (?R) — recurse the entire pattern
  • (?N) — recurse group N
  • (?P>name) or (?&name) — recurse named group

Balanced brackets example

\((?:[^()]++|(?R))*\)

Reading: an opening paren, then any number of (non-paren chars OR a recursive paren-pair), then a closing paren. The (?R) recurses the whole pattern, so it matches nested pairs of any depth.

Engine support

  • PCRE, PHP, Perl: Yes
  • Python regex third-party: Yes
  • Python re: No
  • JavaScript, .NET, Ruby, Java, Go: No

Recursion is one of the least-portable features. If you need it, you're either committed to PCRE OR you should use a real parser.

The rule of thumb

If you're reaching for recursive regex, the problem is probably parsing. Real parsers handle balanced delimiters cleanly without exotic regex features. Use Python ast for code, json for JSON, html.parser for HTML, even hand-written tokenizers for custom DSLs. Recursive regex exists; using it usually means you should have switched tools.

Code

Recursive patterns (PCRE / Python regex)·text
# In PHP / PCRE
preg_match('/\((?:[^()]++|(?R))*\)/', 'foo(bar(baz)qux)quux', $m)
# $m[0] = '(bar(baz)qux)'

# In Python with the third-party 'regex' module
import regex
m = regex.search(r'\((?:[^()]++|(?R))*\)', 'foo(bar(baz)qux)quux')
print(m.group())  # '(bar(baz)qux)'

# In Python's built-in 're' — no recursion support
# would need to write a hand parser

# Recursive named group
pattern = r'(?P<paren>\((?:[^()]++|(?P>paren))*\))'
m = regex.search(pattern, 'foo(bar(baz))')
print(m.group('paren'))

External links

Exercise

Try writing a pattern (in PHP, PCRE-enabled grep, or Python's regex module) that matches balanced JSON-style braces. Then write the equivalent using json.loads. Notice which one handles edge cases (escaped quotes, nested objects, errors) without you having to think about them.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.