Named Captures
Raku's regex engine goes far beyond what you find in most languages. Named captures let you label parts of your match, producing structured data instead of opaque numbered groups. Once you start using them, you will never want to go back to$1, $2, $3.
Basic Named Captures
Use$<name>=[ ... ] to create a named capture:
Named captures are accessed through the match object usingmy $str = "2026-03-22"; if $str ~~ / $<year>=[\d ** 4] '-' $<month>=[\d ** 2] '-' $<day>=[\d ** 2] / { say $<year>; # 2026 say $<month>; # 03 say $<day>; # 22 }
$<name> syntax (or $/<name>).
The Match Object
Every successful match produces aMatch object. Named captures become keys in this object:
Match objects are nested structures. Each named capture is itself amy $m = "Alice: 42" ~~ / $<name>=[\w+] ': ' $<age>=[\d+] /; say $m; # Full match: "Alice: 42" say $m<name>; # "Alice" say $m<age>; # "42" say $m<name>.; # "Alice" as a plain string say $m<age>.; # 42 as an integer say $m.hash.keys; # (name age)
Match object with .from, .to, .Str, and other useful methods.
Captures from Named Rules
When you call a named regex or token, the name automatically becomes a capture:This is how grammars work under the hood. Each rule call creates a named capture in the match tree.my regex year { \d ** 4 } my regex month { \d ** 2 } my regex day { \d ** 2 } if "2026-03-22" ~~ / <year> '-' <month> '-' <day> / { say $<year>; # 2026 say $<month>; # 03 say $<day>; # 22 }
Nested Captures
Named captures can be nested, creating a tree structure:The numbered captures inside a named capture are accessible as children.my $addr = "192.168.1.100:8080"; if $addr ~~ / $<ip>=[ (\d+) ** 4 % '.' ] ':' $<port>=[\d+] / { say $<ip>; # 192.168.1.100 say $<port>; # 8080 say $<ip>[0]; # First octet: 192 say $<ip>[1]; # Second octet: 168 }
Repeated Named Captures
When a named capture repeats, you get an array of matches:Output:my $csv = "Alice,Bob,Carol,Dave"; if $csv ~~ / $<name>=[\w+]+ % ',' / { for $<name>.list -> $n { say "Found: $n"; } }
Found: Alice Found: Bob Found: Carol Found: Dave
Aliasing Captures
You can alias one capture name to another pattern:# Give a meaningful name to a complex pattern my $log = "2026-03-22T14:30:00 ERROR disk full"; if $log ~~ / $<timestamp>=[\S+] \s+ $<level>=[\S+] \s+ $<message>=[\N+] / { say "Time: $<timestamp>"; say "Level: $<level>"; say "Msg: $<message>"; }
Captures with Quantifiers
Named captures interact naturally with quantifiers:my $text = "scores: 10 25 30 45 50"; if $text ~~ / 'scores:' \s+ $<score>=[\d+]+ % \s+ / { my @scores = $<score>.list>>.; say "Scores: @scores[]"; say "Average: {([+] @scores) / @scores.elems}"; }
Accessing Captures Programmatically
You can iterate over all captures in a match:Output:my $m = "name=Alice age=30 city=Toronto" ~~ / [ $<key>=[\w+] '=' $<value>=[\w+] ]+ % \s+ /; for $<key>.list Z $<value>.list -> ($k, $v) { say "$k => $v"; }
name => Alice age => 30 city => Toronto
Non-capturing Groups
Sometimes you need grouping without capturing. Use[ ... ] (square brackets) for non-capturing groups:
# [ ... ] groups without capturing if "hello" ~~ / [he|she] 'llo' / { say "Matched: $/"; # No extra captures } # ( ... ) groups WITH capturing (positional) if "hello" ~~ / (he|she) 'llo' / { say "Captured: $/[0]"; }
Practical Example: Log Parser
Here is a complete log line parser using named captures:my @logs = ( '2026-03-22 14:30:00 [INFO] Server started on port 8080', '2026-03-22 14:30:05 [WARN] High memory usage: 89%', '2026-03-22 14:30:10 [ERROR] Connection refused: db-host', ); for @logs -> $line { if $line ~~ / $<date>=[\d+\-\d+\-\d+] \s+ $<time>=[\d+\:\d+\:\d+] \s+ '[' $<level>=[\w+] ']' \s+ $<message>=[\N+] / { say "{$<level>}: {$<message>}"; } }
Tips
- Use named captures over numbered captures whenever possible. Code that says
$<year>is far clearer than$0. - Match objects are lightweight. Do not be afraid to access
.from,.to,.chars, and.Stron them. - Named captures from grammar rules automatically build your parse tree. This is the foundation for grammar action classes.
Named captures make Raku's regex engine feel more like a parsing framework than a string matching tool. In the next regex post, we will look at subrules for building reusable regex components.