Skip to content
C.W.K.
Stream
Lesson 09 of 12 · published

Possessive Quantifiers — *+ ++ ?+

~10 min · possessive, no-backtrack, performance

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

Disable backtracking for this quantifier

Possessive quantifiers — written as *+, ++, ?+, {n,m}+ — match like greedy, but once they've consumed something, they REFUSE to give it back. No backtracking through this quantifier.

This sounds restrictive, but it's the secret to writing fast, ReDoS-safe patterns. Backtracking is what makes regex slow on adversarial input. Disable backtracking on the parts where you know backtracking won't help, and the engine flies.

The performance win

Pattern a+b against aaaaaaa: greedy a+ grabs all 7 a's, then can't match b, backtracks one a at a time (7 attempts) before failing. Possessive a++b: grabs all 7 a's, can't match b, refuses to backtrack, fails immediately. Same result, much less work.

For ReDoS protection (Track 8), this is everything. Patterns like (a+)+b are exponential because a+ can backtrack into the outer +. Make it possessive — (a++)+b — and it's linear.

Flavor support

Possessive quantifiers exist in PCRE, Java, Ruby (1.9+), and Python 3.11+ built-in re. JavaScript and .NET do not support them. RE2 does not backtrack, so it does not need them.

If your engine doesn't support possessive, the equivalent is the atomic group (?>...), covered in Track 8.

Code

Possessive quantifiers (PCRE/Java/Ruby)·text
# In Java
Pattern.matches("a++b", "aaaaaaa")  // false, no backtracking attempted

# In PHP/PCRE
preg_match('/a++b/', 'aaaaaaa')  // 0

# Python 3.11+ built-in re: possessive quantifier
import re
re.findall(r'a++b', 'aaaaaaa')  # []  — fails fast

# Atomic group equivalent in the same built-in module
import re
re.findall(r'(?>a+)b', 'aaaaaaa')  # Python 3.11+

# Performance difference visible on large input
# 'a' * 30 + 'X'  — adversarial input
# (a+)+ → exponential time, may hang for minutes
# (a++)+ → linear time, fails immediately

External links

Exercise

If you're on Python 3.11+, try re.match(r'(a+)+b', 'a' * 30) and time it. Then try re.match(r'(?>a+)+b', 'a' * 30). The atomic group should finish instantly while the original may hang. This is the ReDoS protection mechanism.

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.