Docs/Data & text

Regular expressions

Find, test, and replace text with patterns.

On this page

valk.regex matches text against a pattern. Compile a pattern once with Regex.new, then reuse it.

Find a match#

Valk
use valk.regex

fn main() {
    let numbers = regex.Regex.new("\\d+") ! {
        println("Invalid pattern")
        return
    }
    let found = numbers.find("Order 42 shipped") ?! return
    println(found.str())   // 42
    println(found.start)   // 6
    println(found.end)     // 8
}

find returns the first match or null. A backslash is doubled inside a Valk string, so \d+ is written "\\d+". Offsets are byte offsets into the text.

Find all, replace, split, test#

Valk
use valk.regex

fn main() {
    let words = regex.Regex.new("[a-z]+") ! return
    each words.find_all("one two three") as found {
        println(found.str())
    }
    let digits = regex.Regex.new("\\d") ! return
    println(digits.replace("a1b2", "#"))   // a#b#
    println(digits.is_match("abc"))        // false

    let comma = regex.Regex.new("\\s*,\\s*") ! return
    println(comma.split("a , b,c").length) // 3
}
Method Returns
find(text, start) First match at or after start, or null
find_all(text, limit) Every match, left to right (limit 0 means all)
match_at(text, start) A match that begins exactly at start, or null
is_match(text) Whether the pattern occurs anywhere
replace(text, replacement, limit) Text with matches replaced
replace_with(text, func, limit) Text with each match replaced by what func returns
split(text) The parts between matches

For a one-off check, regex.is_match(pattern, text) compiles and matches in one call.

Groups#

Valk
use valk.regex

fn main() {
    let mail = regex.Regex.new("(?P<user>\\w+)@(?P<host>[\\w.]+)") ! return
    let found = mail.find("mail me@example.com today") ?! return
    println(found.get(1) ?? "")         // me
    println(found.named("host") ?? "")  // example.com

    println(mail.replace("me@home you@work", "${host}:${user}")) // home:me work:you
}

Group 0 is the whole match. get and named return null for a group that did not take part. A replacement refers to groups as $1 or ${name}.

Replace with a function#

Valk
use valk.regex

fn main() {
    let word = regex.Regex.new("\\b[a-z]") ! return
    let title = word.replace_with("hello big world", fn(m: regex.Match) String {
        return m.str().upper()
    })
    println(title) // Hello Big World
}

Flags and escaping#

Valk
use valk.regex

fn main() {
    let yes = regex.Regex.new("^yes$", "im") ! return
    println(yes.is_match("no\nYES"))  // true
    println(regex.escape("1+1=2"))    // 1\+1=2
}

The flags are i (ignore case), m (^ and $ match at line boundaries) and s (. matches a newline). Inline flags such as (?i) work too. regex.escape makes text match literally.

Syntax#

  • Classes: ., [a-z], [^...], \d \w \s and \D \W \S, [[:alpha:]]
  • Anchors: ^ $ \A \z, word boundaries \b \B
  • Groups: (...), (?:...), (?P<name>...), (?<name>...)
  • Quantifiers: * + ? {n} {n,} {n,m}, add ? for lazy matching
  • Alternation |, leftmost first: a|ab on ab matches a

Matching never backtracks, so a pattern from user input cannot make the program hang. Backreferences, lookaround and \p{...} are not supported: Regex.new throws unsupported, and a malformed pattern throws syntax.