Overview
pcre2 wraps libpcre2 — the industry-standard Perl-compatible regular expression library used by PHP, Python, R, Julia, and many others. It provides the complete Perl 5 regex syntax including features that are deliberately excluded from simpler engines like Go's RE2 or POSIX regex.
Compile a pattern once with Compile or CompileEx, then call Match / MatchAll / Replace / Split as many times as needed. For hot loops, call JITCompile after compilation to get a 10–100× speed boost from the PCRE2 JIT engine.
Requires libpcre2-dev. Ubuntu/Debian: sudo apt install libpcre2-dev
Key Features
| Feature | Syntax | Description |
|---|---|---|
| Named groups | (?P<name>...) or (?<name>...) | Access captures by name with GetNamedGroup |
| Lookahead | (?=...) / (?!...) | Positive and negative lookahead assertions |
| Lookbehind | (?<=...) / (?<!...) | Positive and negative lookbehind assertions |
| Non-greedy | *? +? ?? | Lazy quantifiers; or flip all with Ungreedy flag |
| Unicode | \p{Lu} \p{Script=Latin} | Full UTF-8 and Unicode property escapes with Unicode+UnicodeProps |
| JIT compilation | JITCompile(R) | 10–100× faster repeated matching via native code generation |
| Atomic groups | (?>...) | Prevent backtracking into a subexpression once it has matched |
| Recursion | (?R) (?1) | Match nested structures such as balanced parentheses |
Comparison
| Engine | Lookahead/behind | Named groups | JIT | Unicode props | Notes |
|---|---|---|---|---|---|
| pcre2 | Yes | Yes | Yes | Yes | Full Perl 5 syntax |
| Go regexp (RE2) | No | Yes | No | Partial | Guaranteed linear time; no backtracking |
| Python re | Yes | Yes | No | Yes | Same syntax; PCRE2 JIT faster for hot paths |
| POSIX (ERE) | No | No | No | No | Available everywhere; far more limited |
Quick Start
Compile and match
uses pcre2;
{ Compile once, reuse many times }
var R := Compile('\b\d{4}-\d{2}-\d{2}\b');
var mr := Match(R, 'Invoice date: 2024-07-15, due 2024-08-01');
if mr.Matched then
WriteLn('First date: ' + mr.FullMatch);
{ Find all dates }
var all := MatchAll(R, 'Invoice date: 2024-07-15, due 2024-08-01');
for var i := 0 to Length(all) - 1 do
WriteLn(all[i].FullMatch);
FreeRegex(R);
Named capture groups
uses pcre2;
var R := Compile('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})');
var mr := Match(R, 'Published: 2025-11-03');
if mr.Matched then
begin
WriteLn('Year : ' + GetNamedGroup(mr, 'year'));
WriteLn('Month: ' + GetNamedGroup(mr, 'month'));
WriteLn('Day : ' + GetNamedGroup(mr, 'day'));
end;
FreeRegex(R);
Replace all matches
uses pcre2;
{ Redact credit card numbers — use $1 backreference to keep prefix }
var R := Compile('\b(\d{4})[- ]?\d{4}[- ]?\d{4}[- ]?(\d{4})\b');
var result := ReplaceAll(R, 'Card: 4111 1111 1111 1234',
'$1-****-****-$2');
WriteLn(result); { Card: 4111-****-****-1234 }
FreeRegex(R);
JIT compilation for hot paths
uses pcre2;
if JITSupported then
begin
var R := Compile('^\s*#|^\s*$'); { skip comments and blank lines }
JITCompile(R); { one-time JIT compilation }
var lines := Split(R, logText, 0); { now runs at native speed }
WriteLn(IntToStr(Length(lines)) + ' non-comment lines');
FreeRegex(R);
end;
Types
TPCRE2Regex
type TPCRE2Regex = Integer; { opaque handle to a compiled pattern }
An opaque integer handle returned by Compile or CompileEx. Pass it to any matching or pattern-info function. Free with FreeRegex when done.
TPCRE2Match
type TPCRE2Match = Integer; { opaque handle to a match data block }
Low-level match data block handle. Returned internally; most callers work with TPCRE2MatchResult directly.
TPCRE2Options
type
TPCRE2Options = record
CaseInsensitive : Boolean; { PCRE2_CASELESS }
Multiline : Boolean; { PCRE2_MULTILINE — ^ and $ match line boundaries }
DotAll : Boolean; { PCRE2_DOTALL — dot matches newline }
Extended : Boolean; { PCRE2_EXTENDED — ignore whitespace + # comments }
Unicode : Boolean; { PCRE2_UTF — pattern and subject are UTF-8 }
UnicodeProps : Boolean; { PCRE2_UCP — \d \w \s use Unicode properties }
Ungreedy : Boolean; { PCRE2_UNGREEDY — swap greedy/lazy by default }
UseJIT : Boolean; { compile with JIT }
end;
TPCRE2CaptureGroup
type
TPCRE2CaptureGroup = record
Name : string; { empty if the group is unnamed }
Index : Integer; { group number (1-based; 0 = full match) }
Start : Integer; { byte offset of the group in the subject string }
Length : Integer; { byte length of the matched text }
Value : string; { matched text }
end;
TPCRE2MatchResult
type
TPCRE2MatchResult = record
Matched : Boolean; { True if the pattern matched }
FullMatch : string; { the entire matched substring }
Start : Integer; { byte offset of match start in subject }
Length : Integer; { byte length of the full match }
Groups : array of TPCRE2CaptureGroup; { all capture groups }
end;
Compile API
Compile a pattern once and reuse the handle across many calls. Compiling is expensive; matching is fast.
| Function | Description |
|---|---|
| Compile(Pattern) | Compile Pattern with default options. Returns a TPCRE2Regex handle. |
| CompileEx(Pattern, Opts) | Compile with a TPCRE2Options record for full control over flags. |
| FreeRegex(R) | Release all memory held by the compiled pattern. |
| JITCompile(R) | JIT-compile the already-compiled pattern. Subsequent matches run as native machine code. Check JITSupported first. |
Matching API
All match functions return a TPCRE2MatchResult. Check .Matched before accessing .FullMatch or .Groups.
| Function | Description |
|---|---|
| IsMatch(R, Subject) | Returns True if the pattern matches anywhere in Subject. Fastest check when you don't need captures. |
| Match(R, Subject) | Find the first match and return a full TPCRE2MatchResult with all capture groups. |
| MatchAt(R, Subject, Offset) | Same as Match but start searching at byte Offset in the subject. |
| MatchAll(R, Subject) | Return all non-overlapping matches as an array of TPCRE2MatchResult. |
Capture Groups API
After a successful Match, retrieve individual groups by name or index.
| Function | Description |
|---|---|
| GetNamedGroup(MR, Name) | Return the value of the named capture group Name. Returns empty string if the group did not participate in the match. |
| GetGroup(MR, Index) | Return the value of capture group by number. Index 0 is the full match; 1+ are groups in left-to-right order. |
uses pcre2;
var R := Compile('(?P<proto>https?)://(?P<host>[^/]+)(?P<path>/\S*)?');
var mr := Match(R, 'Visit https://example.com/docs for details');
if mr.Matched then
begin
WriteLn(GetNamedGroup(mr, 'proto')); { https }
WriteLn(GetNamedGroup(mr, 'host')); { example.com }
WriteLn(GetNamedGroup(mr, 'path')); { /docs }
WriteLn(GetGroup(mr, 0)); { full match }
end;
FreeRegex(R);
Replace API
Replacement strings support backreferences: $1, $2 by index, or ${name} for named groups.
| Function | Description |
|---|---|
| Replace(R, Subject, Replacement) | Replace the first match. Returns the modified string. |
| ReplaceAll(R, Subject, Replacement) | Replace all non-overlapping matches. Returns the fully substituted string. |
uses pcre2;
{ Normalize whitespace sequences to a single space }
var R := Compile('\s+');
WriteLn(ReplaceAll(R, ' foo bar baz ', ' ')); { ' foo bar baz ' }
{ Named backreference in replacement }
var R2 := Compile('(?P<last>\w+),\s*(?P<first>\w+)');
WriteLn(ReplaceAll(R2, 'Smith, John', '${first} ${last}')); { John Smith }
FreeRegex(R);
FreeRegex(R2);
Split API
| Function | Description |
|---|---|
| Split(R, Subject, Limit) | Split Subject at every match of R. Limit=0 means unlimited splits; Limit=N produces at most N parts. |
uses pcre2;
{ Split on any run of whitespace or commas }
var R := Compile('[\s,]+');
var tags := Split(R, 'alpha, beta gamma,delta', 0);
for var i := 0 to Length(tags) - 1 do
WriteLn(tags[i]); { alpha / beta / gamma / delta }
FreeRegex(R);
One-Shot Helpers
Convenience functions that compile, execute, and free the pattern in one call. Convenient for ad-hoc use; prefer pre-compiled patterns in loops.
| Function | Description |
|---|---|
| QuickMatch(Pattern, Subject) | Compile, match first occurrence, free. Returns TPCRE2MatchResult. |
| QuickTest(Pattern, Subject) | Compile, test for any match, free. Returns Boolean. |
| QuickReplace(Pattern, Subject, Replacement) | Compile, replace all matches, free. Returns result string. |
uses pcre2;
{ One-liner email check }
if QuickTest('^[\w.+-]+@[\w-]+\.[a-z]{2,}$', email) then
WriteLn('Valid email');
{ One-liner replace }
var slug := QuickReplace('[^a-z0-9]+',
LowerCase(title), '-');
Pattern Info API
| Function | Description |
|---|---|
| CaptureCount(R) | Returns the number of capture groups in the compiled pattern. |
| NamedGroups(R) | Returns the names of all named capture groups as array of string. |
| JITSupported | Returns True if the PCRE2 JIT engine is available on this platform. |
| PCRE2Version | Returns the version string of the linked libpcre2, e.g. "10.42 2022-12-11". |
uses pcre2;
var R := Compile('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})');
WriteLn('Groups : ' + IntToStr(CaptureCount(R))); { 3 }
var names := NamedGroups(R);
for var i := 0 to Length(names) - 1 do
WriteLn(names[i]); { year / month / day }
WriteLn('PCRE2 : ' + PCRE2Version);
FreeRegex(R);
Calling Compile parses the pattern and builds an internal NFA/DFA structure. For any pattern used more than once — including inside loops — compile before the loop and free after. The Quick* helpers re-compile every call and are only suitable for infrequent use.
Package Info
System Requirements
libpcre2-devUbuntu/Debian:
sudo apt install libpcre2-dev
TPCRE2Options Flags
CaseInsensitive — PCRE2_CASELESSMultiline — PCRE2_MULTILINEDotAll — PCRE2_DOTALLExtended — PCRE2_EXTENDEDUnicode — PCRE2_UTFUnicodeProps — PCRE2_UCPUngreedy — PCRE2_UNGREEDYUseJIT — JIT on compile
Functions
- Compile
- CompileEx
- FreeRegex
- JITCompile
- IsMatch
- Match
- MatchAt
- MatchAll
- GetNamedGroup
- GetGroup
- Replace
- ReplaceAll
- Split
- QuickMatch
- QuickTest
- QuickReplace
- CaptureCount
- NamedGroups
- JITSupported
- PCRE2Version
See Also
- regex (simple RE2-style)
- string-utils