Regex Basics in Raku
2026-03-16
If you have used regular expressions in Perl, Python, or JavaScript, Raku's regexes will feel familiar in purpose but refreshingly different in syntax. Raku redesigned regular expressions from scratch to be more readable, more powerful, and easier to maintain. Whitespace is ignored by default (making complex patterns readable), and the syntax is more consistent. Let us learn Raku regexes from the ground up.
Your First Match
The smart match operator checks if a string matches a regex:
my $text = "Hello, World!";
if $text ~~ /World/ {
say "Found it!";
}
if $text ~~ /Raku/ {
say "Found Raku";
} else {
say "No Raku here";
}
The ! operator is the negated form:
if "hello123" !~~ /^\d+$/ {
say "Not all digits";
}
Whitespace in Raku Regexes
This is the biggest difference from traditional regexes. In Raku, whitespace inside a regex is ignored by default. This lets you spread out complex patterns for readability:
/hello world/
/hello ' ' world/
/hello \s world/
To match a literal space, you must quote it or use \s. This is a deliberate design choice that makes complex regexes much easier to read.
Literal Matching
To match literal text, just write it. For characters that have special meaning, quote them:
say "abc" ~~ /abc/;
say "hello" ~~ /hell/;
say "3.14" ~~ / 3 '.' 14 /;
say "3.14" ~~ / 3 \. 14 /;
say "$100" ~~ / '$' 100 /;
Anchors
Anchors match positions, not characters:
say "hello" ~~ /^ hello $/;
say "hello world" ~~ /^ hello/;
say "hello world" ~~ /world $/;
my $text = "line one\nline two\nline three";
say $text ~~ /$$ three/;
Character Classes
Raku uses <[ ]> for character classes instead of the traditional [ ]:
say "hello" ~~ /<[aeiou]>/;
say "abc123" ~~ /<[0..9]>/;
say "hello" ~~ /<[a..z]>/;
say "hello" ~~ /<-[aeiou]>/;
Character Class Operations
You can combine character classes with set operations:
/<[aeiou] + [0..9]>/
/<[a..z] - [aeiou]>/
/\d/
/\w/
/\s/
/\D/
/\W/
/\S/
/./
Quantifiers
Quantifiers control how many times a pattern matches:
my $text = "aabbbcccc";
say $text ~~ /a+/;
say $text ~~ /a*b/;
say $text ~~ /d?/;
say $text ~~ /b ** 3/;
say $text ~~ /c ** 2..4/;# True: 2 to 4 'c's
Summary:
| Quantifier |
Meaning |
|
zero or more |
+ |
one or more |
? |
zero or one |
N |
exactly N times |
N..M |
between N and M times |
N.. |
N or more times |
Greedy vs Frugal
By default, quantifiers are greedy (they match as much as possible). Add ? to make them frugal (match as little as possible):
my $html = '<b>bold</b> and <b>more bold</b>';
if $html ~~ /'<b>' .* '</b>'/ {
say ~$/;
}
if $html ~~ /'<b>' .*? '</b>'/ {
say ~$/;
}
Captures
Parentheses create numbered captures, accessible as $0, $1, etc.:
my $date = "2026-03-16";
if $date ~~ /(\d ** 4) '-' (\d ** 2) '-' (\d ** 2)/ {
say "Year: $0";
say "Month: $1";
say "Day: $2";
}
The match result is stored in the special variable $/:
"Hello World" ~~ /(\w+) \s+ (\w+)/;
say $/[0];
say $/[1];
say ~$/;
Named Captures
Named captures use $<name> syntax and are much more readable:
my $line = "Alice: 95";
if $line ~~ /$<name>=(\w+) ':' \s* $<score>=(\d+)/ {
say "Student: $<name>";
say "Score: $<score>";
}
Built-in Named Patterns
Raku provides several useful predefined patterns:
say "hello123" ~~ /<alpha>/;
say "hello123" ~~ /<digit>/;
say "hello123" ~~ /<alnum>/;
say " hello" ~~ /<space>/;
say "HELLO" ~~ /<upper>/;
say "hello" ~~ /<lower>/;
Use + with named patterns for matching multiple characters:
"hello123" ~~ /<alpha>+/;
say ~$/;
"hello123" ~~ /<digit>+/;
say ~$/;
Modifiers (Adverbs)
Raku regex adverbs modify matching behavior:
:i (case insensitive)
say "Hello" ~~ /:i hello/;
say "HELLO" ~~ /:i hello/;
:g (global matching)
my $text = "cat bat hat mat";
my @matches = $text ~~ m:g/\w+ 'at'/;
say @matches.elems;
say @matches;
:s (significant whitespace)
Normally whitespace in regexes is ignored. The :s adverb makes whitespace match \s+ (one or more whitespace characters):
my $text = "Hello World";
say $text ~~ /Hello World/;
say $text ~~ /:s Hello World/;
Combining Adverbs
my $text = "The Cat sat on the Mat";
my @found = $text ~~ m:i:g/ <[cm]> at /;
say @found;
The .match Method
You can also use the .match method on strings:
my $result = "Hello World".match(/(\w+) \s+ (\w+)/);
if $result {
say $result[0];
say $result[1];
}
For global matches, pass :g:
my @all = "one 1 two 2 three 3".match(/:g \d+/);
say @all;
Alternation
Use | for longest-match alternation and || for first-match:
say "railroad" ~~ / rail | railroad /;
say ~$/;
say "railroad" ~~ / rail || railroad /;
say ~$/;
This distinction matters when alternatives overlap:
my $file = "photo.jpeg";
if $file ~~ / '.' (jpg | jpeg | png | gif) $/ {
say "Image format: $0";
}
Practical Example: Email Validator
my @emails = (
'alice@example.com',
'bob.smith@company.co.uk',
'invalid@',
'@nouser.com',
'spaces in@address.com',
'good_one+tag@gmail.com',
'missing.domain@',
);
my $email-rx = /^
<[\w.+\-]>+
'@'
<[\w.\-]>+
'.'
<[a..zA..Z]> ** 2..10
$/;
for @emails -> $addr {
my $status = $addr ~~ $email-rx ?? "VALID" !! "INVALID";
say sprintf("%-30s %s", $addr, $status);
}
Output:
alice@example.com VALID
bob.smith@company.co.uk VALID
invalid@ INVALID
@nouser.com INVALID
spaces in@address.com INVALID
good_one+tag@gmail.com VALID
missing.domain@ INVALID
Practical Example: Log Line Parser
my @logs = (
'2026-03-16 10:30:15 [INFO] Server started on port 8080',
'2026-03-16 10:30:22 [ERROR] Connection refused: 10.0.0.5:3306',
'2026-03-16 10:31:05 [WARN] Memory usage at 85%',
);
for @logs -> $line {
if $line ~~ /
$<date> = (\d ** 4 '-' \d ** 2 '-' \d ** 2) \s+
$<time> = (\d ** 2 ':' \d ** 2 ':' \d ** 2) \s+
'[' $<level> = (\w+) ']' \s+
$<message> = (.+)
/ {
say "Level: $<level>";
say "Time: $<date> $<time>";
say "Message: $<message>";
say "---";
}
}
What is Next?
You now have a solid foundation in Raku regex basics. There is much more to explore (grammars, tokens, rules), but what you have learned here will handle the vast majority of text matching and extraction tasks. Next up: file I/O in Raku, where we will read, write, and navigate the filesystem.