Mojo Regular Expressions
A regular expression (regex) is a pattern that describes a set of strings. You use regexes to search for text, validate input, extract parts of strings, and replace content. Mojo accesses the full Python re module through its interop layer, giving you one of the most powerful and battle-tested regex engines available.
The Pattern-Matching Concept
Text: "Call us at 555-7890 or 800-1234"
Pattern: \d{3}-\d{4}
Matches found:
"Call us at [555-7890] or [800-1234]"
───────── ─────────
match 1 match 2
The pattern \d{3}-\d{4} means:
\d{3} → exactly 3 digits
- → a literal hyphen
\d{4} → exactly 4 digits
Importing the re Module
from python import Python
fn main() raises:
var re = Python.import_module("re")
print("re module loaded:", re.__name__)
Pattern Syntax Cheat Sheet
Pattern | Matches
-----------|-----------------------------------------------
. | Any character except newline
\d | One digit [0-9]
\D | One non-digit
\w | One word character [a-zA-Z0-9_]
\W | One non-word character
\s | One whitespace (space, tab, newline)
\S | One non-whitespace
^ | Start of string
$ | End of string
* | Zero or more of preceding
+ | One or more of preceding
? | Zero or one of preceding
{n} | Exactly n of preceding
{n,m} | Between n and m of preceding
[abc] | One of: a, b, or c
[^abc] | One character that is NOT a, b, or c
(group) | Capture a group
a|b | Either a or b
Checking if a Pattern Exists: re.search
from python import Python
fn main() raises:
var re = Python.import_module("re")
var text = "The temperature is 37.5 degrees"
var match = re.search(r"\d+\.?\d*", text)
if match:
print("Number found:", match.group()) # 37.5
else:
print("No number found")
Matching from the Start: re.match
from python import Python
fn main() raises:
var re = Python.import_module("re")
# re.match only checks the beginning of the string
var m1 = re.match(r"\d+", "42 apples")
var m2 = re.match(r"\d+", "apples 42")
if m1: print("m1 matched:", m1.group()) # 42
if not m2: print("m2 not matched — no digits at start")
Finding All Matches: re.findall
from python import Python
fn main() raises:
var re = Python.import_module("re")
var sentence = "Prices: $12, $450, $3, $1200"
var amounts = re.findall(r"\$(\d+)", sentence)
print(amounts) # ['12', '450', '3', '1200']
print(len(amounts)) # 4
var total = 0
for i in range(int(len(amounts))):
total += int(amounts[i])
print("Total: $" + str(total)) # Total: $1665
findall() diagram:
Text: "Prices: $12, $450, $3, $1200"
Pattern: \$(\d+)
↑ ↑ ↑ ↑
Captures: 12 450 3 1200
Returns: ['12', '450', '3', '1200']
Replacing Text: re.sub
from python import Python
fn main() raises:
var re = Python.import_module("re")
var text = "My phone: 555-123-4567 or 800-987-6543"
# Mask all digits with *
var masked = re.sub(r"\d", "*", text)
print(masked) # My phone: ***-***-**** or ***-***-****
# Replace phone numbers with [REDACTED]
var redacted = re.sub(r"\d{3}-\d{3}-\d{4}", "[REDACTED]", text)
print(redacted) # My phone: [REDACTED] or [REDACTED]
Splitting Strings: re.split
from python import Python
fn main() raises:
var re = Python.import_module("re")
# Split on any whitespace or punctuation
var csv_like = "Alice,30;Engineer|Tokyo"
var parts = re.split(r"[,;|]", csv_like)
for i in range(int(len(parts))):
print(parts[i])
# Alice
# 30
# Engineer
# Tokyo
Capturing Groups
from python import Python
fn main() raises:
var re = Python.import_module("re")
var log_line = "2025-03-15 ERROR DatabaseConnection: timeout after 30s"
var pattern = r"(\d{4}-\d{2}-\d{2}) (\w+) (.+)"
var m = re.match(pattern, log_line)
if m:
print("Date: ", m.group(1)) # 2025-03-15
print("Level: ", m.group(2)) # ERROR
print("Message: ", m.group(3)) # DatabaseConnection: timeout after 30s
Group diagram:
"2025-03-15 ERROR DatabaseConnection: timeout after 30s"
────────── ───── ─────────────────────────────────────
group(1) group(2) group(3)
(\d{4}-\d{2}-\d{2}) → date
(\w+) → log level
(.+) → rest of line
Compiling Patterns for Speed
Compile a pattern once when you run the same regex many times — this avoids re-parsing the pattern on every call.
from python import Python
fn main() raises:
var re = Python.import_module("re")
# Compile once
var email_pattern = re.compile(
r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
)
var texts = List[String](
"Contact me at alice@example.com",
"No email here",
"Send to bob.smith@company.org please",
)
for i in range(len(texts)):
var m = email_pattern.search(texts[i])
if m:
print("Found:", m.group())
else:
print("No email in:", texts[i])
Common Regex Patterns
Use case | Pattern
--------------------|------------------------------------------------
Email address | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
Phone (US) | \(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
Date (YYYY-MM-DD) | \d{4}-\d{2}-\d{2}
IPv4 address | \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
Positive integer | \d+
Decimal number | -?\d+\.?\d*
Hex color | #[0-9a-fA-F]{6}
URL | https?://[^\s]+
HTML tag | <[^>]+>
Whitespace run | \s+
Key Takeaways
Import Python's re module with Python.import_module("re"). Use re.search to find a pattern anywhere in a string, re.match to match only at the start, and re.findall to extract all matches into a list. Replace text with re.sub and split strings on regex delimiters with re.split. Parentheses create capture groups accessible via match.group(n). Compile patterns with re.compile when using the same pattern repeatedly. Use raw strings (r"...") for patterns to avoid Python interpreting backslashes as escape characters.
