Regex flavors compared
On this page
There is no single regex spec. Each language and engine has its own flavor with subtle differences. Here’s a side-by-side reference for the seven you’ll meet most often, and the practical rules for porting patterns between them.
At a glance
| Feature | JS | Python re | Python regex | PCRE | RE2 (Go) | .NET | POSIX ERE |
|---|---|---|---|---|---|---|---|
| Backreferences | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |
| Named captures | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| Lookahead | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
| Lookbehind | ✓ | fixed-length | ✓ | ✓ | ✗ | ✓ | ✗ |
Atomic groups (?>…) | ✗ | ✗ | ✓ | ✓ | ✗ | ✓ | ✗ |
Possessive a++ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
Recursion (?R) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
Conditional (?(1)…) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✗ |
Unicode categories \p{...} | with u flag | ✓ | ✓ | ✓ | ✓ | ✓ | varies |
Inline flags (?i) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| Linear-time guarantee | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | varies |
Two takeaways:
- PCRE is the kitchen sink. It supports almost every feature.
- RE2 is the strict minimalist. No backrefs, no lookaround — but guaranteed linear time, no catastrophic backtracking.
Most flavors fall between these poles.
JavaScript (ECMAScript)
What our tester uses. The default flavor for browser and Node code.
const re = /\b(\w+)@(\w+\.\w+)\b/gi;
const matches = [...text.matchAll(re)];
Has: lookarounds (all four directions, variable-length lookbehind
since ES2018), named captures (?<name>...), Unicode mode u, sticky
flag y, indices flag d.
Doesn’t have: atomic groups, possessive quantifiers, recursion,
conditionals, inline flags. Older browsers may not support \p{...}
even with u.
Use it for: browser code, Node servers, anything where regex runs against user input that could be unbounded.
Python re
The standard library. PCRE-ish.
import re
m = re.search(r"\b(\w+)@(\w+\.\w+)\b", text, re.IGNORECASE)
re.findall(r"\d+", text)
Has: named captures (?P<name>...) (note the P), inline flags
(?i), fixed-length lookbehind, Unicode categories.
Doesn’t have: variable-length lookbehind, atomic groups, possessive
quantifiers, recursion. Backreferences via \1 not $1.
Note: Python uses (?P<name>...) (with P) for named groups
historically; modern Python 3.11+ also accepts the JS-style
(?<name>...).
Python regex (third-party)
The regex package on PyPI is a near-superset of re plus PCRE
features.
import regex
m = regex.search(r"(?P<word>\w+)@(?P=word)", text) # backreference by name
Adds vs re: atomic groups, possessive quantifiers, variable-length
lookbehind, fuzzy matching, set operations on character classes.
When PCRE features matter and you’re in Python, install regex.
PCRE (Perl-Compatible Regular Expressions)
The original “kitchen sink”. Used by grep -P, PHP’s preg_*, many
editors’ “advanced” regex modes.
Has everything in this comparison. Recursion (?R), conditionals
(?(1)yes|no), atomic groups, possessive quantifiers, callouts.
The cost: PCRE patterns can be exponential-time on adversarial input. For untrusted patterns or untrusted inputs, prefer RE2.
RE2 (used by Go’s regexp, also a standalone library)
Designed for guaranteed linear-time matching — the engine refuses to support features that could cause catastrophic backtracking.
re := regexp.MustCompile(`\b(\w+)@(\w+\.\w+)\b`)
matches := re.FindAllStringSubmatch(text, -1)
Has: named captures, Unicode categories, inline flags, all common quantifiers and character classes.
Doesn’t have: backreferences, lookaround. Period. By design.
Use it for: Go server code, security-sensitive matching (where adversarial input shouldn’t be able to DoS your regex engine), or any high-throughput pattern matching.
.NET (C#, F#)
Microsoft’s regex implementation. Surprisingly capable.
var re = new Regex(@"\b(?<user>\w+)@(?<domain>\w+\.\w+)\b", RegexOptions.IgnoreCase);
var match = re.Match(text);
match.Groups["user"].Value;
Has: variable-length lookbehind (since .NET 1.0!), named captures, balancing groups (a unique .NET feature for matching nested structures), right-to-left matching, conditionals.
Doesn’t have: PCRE recursion, possessive quantifiers (uses atomic groups instead).
POSIX BRE / ERE
The “ancient” regex flavors. Used by traditional Unix tools.
grep -E "\b\d{3}-\d{2}-\d{4}\b" # ERE — modern grep
grep "\<[0-9]\{3\}\>" # BRE — note backslashed parens/braces
Has: the basics — character classes, alternation, groups, anchors, quantifiers (different escaping rules in BRE vs ERE).
Doesn’t have: lookaround, backreferences (BRE has them, ERE technically doesn’t), named groups, Unicode categories. Limited character class support.
Use it for: shell scripting where portability matters more than features.
Porting patterns
From PCRE to JavaScript
Common adjustments:
(?>...) → rewrite without atomic groups
a++ → rewrite without possessive quantifiers
(?R) → recursion not available; use a parser
(?<=foo|bar) → variable-length lookbehind works in JS (since ES2018)
\A, \z, \Z → use ^ and $ instead
(?i)pattern → use the i flag
\p{Greek} → works with the u flag
From Python re to JavaScript
(?P<name>...) → (?<name>...)
\1, \2 → backref syntax is the same in patterns
re.IGNORECASE → /pattern/i
re.MULTILINE → /pattern/m
re.DOTALL → /pattern/s
re.UNICODE → /pattern/u (now default in Python 3, opt-in in JS)
From any flavor to RE2 (Go)
The hard direction. RE2 doesn’t support:
- Backreferences (
\1,\k<name>) — rewrite without them, often by matching twice and comparing in code. - Lookarounds — rewrite by matching the larger context and using capture groups to extract the part you want.
If you can’t avoid these, you can’t use Go’s stdlib regex. Drop to a PCRE-via-cgo binding or use a different approach.
Practical rules
- Default to JavaScript regex if you’re writing a pattern for the web and need lookarounds/named captures.
- Default to RE2 / Go regex if you’re processing untrusted patterns or ultra-high-throughput data. The linear-time guarantee is real.
- Use PCRE / Python
regexfor one-off ETL or code analysis where you need every feature. - Don’t write the regex twice for two flavors. Pick one canonical form and either port at build time or wrap it in language-specific regex objects.
Try the tools
- Live tester (JavaScript flavor) — what your browser sees
- Find and replace — capture-group substitution
- Cheat sheet — every JS regex token