Skip to content
AgileLogics
6 min read

Strings and Regular Expressions

Prerequisites: Strings and characters

A regex literal is written #"..." — backslashes don't need doubling, so #"\d+" means "one or more digits". It compiles to a Java java.util.regex.Pattern.

(println (re-find #"\d+" "order 66 shipped"))          ; first match
(println (re-matches #"\d{3}-\d{4}" "555-1234"))       ; whole string must match
(println (re-matches #"\d{3}-\d{4}" "call 555-1234"))
(println (re-seq #"\d+" "1 fish, 22 fish, 333 fish"))  ; all matches
(println (re-find #"(\w+)@(\w+)\.com" "mail ada@example.com"))
(println (re-pattern (str "^" "v" "\\d+$")))
66
555-1234
nil
(1 22 333)
[ada@example.com ada example]
#"^v\d+$"
  • re-find returns the first match — or, if the pattern has groups, a vector [whole-match group1 group2 ...].
  • re-matches requires the entire string to match.
  • re-seq returns a lazy sequence of all matches.
  • re-pattern builds a pattern from a string (then backslashes must be doubled).

Try patterns interactively with the Regex Tester.

Parsing with groups

(def log-line "2026-09-29 ERROR [db] connection lost")

(when-let [[_ date level source msg] (re-matches #"(\S+) (\w+) \[(\w+)\] (.*)" log-line)]
  (println {:date date :level (keyword (clojure.string/lower-case level))
            :source source :message msg}))

(println (for [[_ id status] (re-seq #"#(\d+) (\w+)" "#123 shipped, #456 pending")]
           {:id (parse-long id) :status (keyword status)}))
{:date 2026-09-29, :level :error, :source db, :message connection lost}
({:id 123, :status :shipped} {:id 456, :status :pending})

when-let binds a name and runs its body only if the value is truthy — perfect for "if it matched, destructure the groups".

Replacing and splitting

(require '[clojure.string :as str])

(println (str/replace "hello world" #"o" "0"))
(println (str/replace "2026-09-29" #"(\d+)-(\d+)-(\d+)" "$3.$2.$1"))
(println (str/replace "hello clojure world" #"\b\w" str/upper-case))
(println (str/split "a1b22c333" #"\d+"))
(println (str/split "one  two   three" #"\s+"))
(println (str/split-lines "first\nsecond\r\nthird"))
(println (str/replace-first "a-b-c" "-" "+"))
hell0 w0rld
29.09.2026
Hello Clojure World
[a b c]
[one two three]
[first second third]
a+b-c

str/replace takes a string, a regex with $1-style group references, or a function that receives each match and returns its replacement.

Common mistakes

  • re-matches vs. re-find: use re-matches for validation, so "abc123" doesn't pass a digits-only check.
  • Case-insensitive matching needs the (?i) flag: #"(?i)error".

Practice

Extract all email addresses from "Contact ada@example.com or ops@test.org today" and print their domains.

Show solution
(def text "Contact ada@example.com or ops@test.org today")
(def emails (re-seq #"[\w.]+@[\w.]+\w" text))
(println emails)
(println (map #(second (clojure.string/split % #"@")) emails))
(ada@example.com ops@test.org)
(example.com test.org)