Strings & Characters
Core String Operations
string/split
Split a string by a delimiter.
(string/split "a,b,c" ",") ; => ("a" "b" "c")
(string/split "hello world" " ") ; => ("hello" "world")string/join
Join a list of strings with a separator.
(string/join '("a" "b" "c") ", ") ; => "a, b, c"
(string/join '("x" "y") "-") ; => "x-y"string/trim
Remove whitespace from both ends.
(string/trim " hello ") ; => "hello"
(string/trim "\thello\n") ; => "hello"string/trim-left
Remove whitespace from the left.
(string/trim-left " hi") ; => "hi"string/trim-right
Remove whitespace from the right.
(string/trim-right "hi ") ; => "hi"string/upper
Convert string to uppercase.
(string/upper "hello") ; => "HELLO"string/lower
Convert string to lowercase.
(string/lower "HELLO") ; => "hello"string/capitalize
Capitalize the first character.
(string/capitalize "hello") ; => "Hello"string/title-case
Capitalize the first character of each word.
(string/title-case "hello world") ; => "Hello World"string/contains?
Test if a string contains a substring.
(string/contains? "hello" "ell") ; => #t
(string/contains? "hello" "xyz") ; => #fstring/starts-with?
Test if a string starts with a prefix.
(string/starts-with? "hello" "he") ; => #t
(string/starts-with? "hello" "lo") ; => #fstring/ends-with?
Test if a string ends with a suffix.
(string/ends-with? "hello" "lo") ; => #t
(string/ends-with? "hello" "he") ; => #fstring/replace
Replace all occurrences of a substring.
(string/replace "hello" "l" "r") ; => "herro"
(string/replace "aaa" "a" "b") ; => "bbb"string/index-of
Return the byte index of the first occurrence of a substring, or nil if not found.
(string/index-of "hello" "ll") ; => 2
(string/index-of "hello" "xyz") ; => nilstring/last-index-of
Find the last occurrence of a substring. Returns the index or -1 if not found.
(string/last-index-of "abcabc" "abc") ; => 3
(string/last-index-of "hello" "xyz") ; => -1string/chars
Convert a string to a list of characters.
(string/chars "abc") ; => (#\a #\b #\c)string/repeat
Repeat a string N times.
(string/repeat "ab" 3) ; => "ababab"
(string/repeat "-" 5) ; => "-----"string/pad-left
Pad a string on the left to a given width.
(string/pad-left "42" 5 "0") ; => "00042"
(string/pad-left "hi" 5) ; => " hi"string/pad-right
Pad a string on the right to a given width.
(string/pad-right "hi" 5) ; => "hi "
(string/pad-right "42" 5 "0") ; => "42000"string/number?
Test if a string represents a valid number.
(string/number? "42") ; => #t
(string/number? "3.14") ; => #t
(string/number? "hello") ; => #fstring/empty?
Test if a string is empty.
(string/empty? "") ; => #t
(string/empty? "hello") ; => #fstring/map
Apply a character function to each character in a string, returning a new string.
(string/map char-upcase "hello") ; => "HELLO"string/reverse
Reverse a string.
(string/reverse "hello") ; => "olleh"Unicode & Encoding
string/byte-length
Return the UTF-8 byte length of a string (as opposed to character count from string-length). Useful for understanding the actual memory footprint — emoji and CJK characters use more bytes than ASCII.
(string/byte-length "hello") ; => 5 (ASCII: 1 byte each)
(string/byte-length "héllo") ; => 6 (é is 2 bytes in UTF-8)
(string/byte-length "日本語") ; => 9 (CJK: 3 bytes each)
(string/byte-length "😀") ; => 4 (emoji: 4 bytes)Compare with string-length which counts characters:
(string-length "😀") ; => 1 (one character)
(string/byte-length "😀") ; => 4 (four bytes)string/codepoints
Return a list of Unicode codepoint integers for each character in a string. This reveals the internal structure of composed characters and emoji sequences.
(string/codepoints "ABC") ; => (65 66 67)
(string/codepoints "é") ; => (233)
(string/codepoints "😀") ; => (128512)Emoji that appear as a single glyph are often multiple codepoints joined by Zero Width Joiner (U+200D = 8205):
;; 👨👩👦 is actually 👨 + ZWJ + 👩 + ZWJ + 👦
(string/codepoints "👨👩👦") ; => (128104 8205 128105 8205 128102)
;; 👋🏽 is 👋 + skin tone modifier
(string/codepoints "👋🏽") ; => (128075 127997)string/from-codepoints
Construct a string from a list of Unicode codepoint integers. This is the inverse of string/codepoints and enables building emoji programmatically by combining codepoints.
(string/from-codepoints (list 65 66 67)) ; => "ABC"
(string/from-codepoints (list 233)) ; => "é"Build emoji by combining people with ZWJ (8205):
;; Build a family: 👨 + ZWJ + 👩 + ZWJ + 👧
(string/from-codepoints (list 128104 8205 128105 8205 128103))
;; => 👨👩👧
;; Build a profession: 👩 + ZWJ + 💻
(string/from-codepoints (list 128105 8205 128187))
;; => 👩💻
;; Add skin tone: 👋 + modifier
(string/from-codepoints (list 128075 127997))
;; => 👋🏽
;; Build flags from Regional Indicators (A=127462):
(string/from-codepoints (list 127475 127476))
;; => 🇳🇴 (NO = Norway)Roundtrip any string through codepoints:
(string/from-codepoints (string/codepoints "Hello 世界"))
;; => "Hello 世界"string/normalize
Normalize a string to a Unicode normalization form. Supported forms: :nfc, :nfd, :nfkc, :nfkd (as keywords or strings).
- NFC — Canonical Decomposition, followed by Canonical Composition (most common)
- NFD — Canonical Decomposition
- NFKC — Compatibility Decomposition, followed by Canonical Composition
- NFKD — Compatibility Decomposition
;; NFC: combine decomposed characters
;; e + combining acute accent → é
(string/normalize "e\u0301" :nfc) ; => "é"
;; NFD: decompose composed characters
(string-length (string/normalize "é" :nfd)) ; => 2 (e + combining accent)
;; NFKC/NFKD: compatibility decomposition (ligatures, etc.)
(string/normalize "\uFB01" :nfkc) ; => "fi" (fi ligature → two letters)
;; String form names also work
(string/normalize "e\u0301" "NFC") ; => "é"string/foldcase
Apply Unicode case folding to a string. Useful for case-insensitive comparisons and normalization. Uses full Unicode-aware lowercasing.
(string/foldcase "HELLO") ; => "hello"
(string/foldcase "Hello World") ; => "hello world"
(string/foldcase "Straße") ; => "straße"
(string/foldcase "ΩΜΕΓΑ") ; => "ωμεγα"string-ci=?
Case-insensitive string equality comparison. Compares two strings after applying case folding to both.
(string-ci=? "Hello" "hello") ; => #t
(string-ci=? "ABC" "abc") ; => #t
(string-ci=? "CAFÉ" "café") ; => #t
(string-ci=? "hello" "world") ; => #fScheme Compatibility Aliases
These functions use legacy Scheme/R7RS naming conventions. They work identically to their modern equivalents and are kept for compatibility. Prefer the string/ namespaced variants in new code.
string-append
Concatenate strings together.
(string-append "hello" " " "world") ; => "hello world"
(string-append "a" "b" "c") ; => "abc"string-length
Return the number of characters in a string.
(string-length "hello") ; => 5
(string-length "") ; => 0
(string-length "héllo") ; => 5
(string-length "日本語") ; => 3string-ref
Return the character at a given index.
(string-ref "hello" 0) ; => #\h
(string-ref "hello" 4) ; => #\osubstring
Extract a substring by start and end character index.
(substring "hello" 1 3) ; => "el"
(substring "hello" 0 5) ; => "hello"
(substring "héllo" 1 2) ; => "é"str
Convert any value to its string representation.
(str 42) ; => "42"
(str #t) ; => "#t"
(str '(1 2 3)) ; => "(1 2 3)"format
Format a string with ~a placeholders.
(format "~a is ~a" "Sema" "great") ; => "Sema is great"
(format "~a + ~a = ~a" 1 2 3) ; => "1 + 2 = 3"Characters
Character literals are written with the #\ prefix.
#\a ; character literal
#\space ; named character: space
#\newline ; named character: newline
#\tab ; named character: tabchar->integer
Convert a character to its Unicode code point.
(char->integer #\A) ; => 65
(char->integer #\a) ; => 97integer->char
Convert a Unicode code point to a character.
(integer->char 65) ; => #\A
(integer->char 955) ; => #\λchar-alphabetic?
Test if a character is alphabetic.
(char-alphabetic? #\a) ; => #t
(char-alphabetic? #\5) ; => #fchar-numeric?
Test if a character is numeric.
(char-numeric? #\5) ; => #t
(char-numeric? #\a) ; => #fchar-whitespace?
Test if a character is whitespace.
(char-whitespace? #\space) ; => #t
(char-whitespace? #\a) ; => #fchar-upper-case?
Test if a character is uppercase.
(char-upper-case? #\A) ; => #t
(char-upper-case? #\a) ; => #fchar-upcase
Convert a character to uppercase.
(char-upcase #\a) ; => #\Achar-downcase
Convert a character to lowercase.
(char-downcase #\Z) ; => #\zchar->string
Convert a character to a single-character string.
(char->string #\a) ; => "a"string->char
Convert a single-character string to a character.
(string->char "a") ; => #\aCharacter Comparison (R7RS)
char=?
Character equality.
(char=? #\a #\a) ; => #t
(char=? #\a #\b) ; => #fchar<?
Character less-than (by code point).
(char<? #\a #\b) ; => #tchar>?
Character greater-than.
(char>? #\b #\a) ; => #tchar<=?
Character less-than-or-equal.
(char<=? #\a #\b) ; => #t
(char<=? #\a #\a) ; => #tchar>=?
Character greater-than-or-equal.
(char>=? #\b #\a) ; => #tchar-ci=?
Case-insensitive character equality.
(char-ci=? #\A #\a) ; => #tType Conversions
string->number
Parse a string as a number.
(string->number "42") ; => 42
(string->number "3.14") ; => 3.14number->string
Convert a number to a string.
(number->string 42) ; => "42"
(number->string 3.14) ; => "3.14"string->symbol
Convert a string to a symbol.
(string->symbol "foo") ; => foosymbol->string
Convert a symbol to a string.
(symbol->string 'foo) ; => "foo"string->keyword
Convert a string to a keyword.
(string->keyword "name") ; => :namekeyword->string
Convert a keyword to a string.
(keyword->string :name) ; => "name"string->list
Convert a string to a list of characters.
(string->list "abc") ; => (#\a #\b #\c)list->string
Convert a list of characters to a string.
(list->string '(#\h #\i)) ; => "hi"Slicing & Extraction
string/after
Everything after the first occurrence of a needle. Returns the original string if needle not found.
(string/after "hello@world.com" "@") ; => "world.com"
(string/after "no-match" "@") ; => "no-match"string/after-last
Everything after the last occurrence of a needle.
(string/after-last "a.b.c" ".") ; => "c"string/before
Everything before the first occurrence of a needle.
(string/before "hello@world.com" "@") ; => "hello"
(string/before "no-match" "@") ; => "no-match"string/before-last
Everything before the last occurrence of a needle.
(string/before-last "a.b.c" ".") ; => "a.b"string/between
Extract the portion between two delimiters.
(string/between "[hello]" "[" "]") ; => "hello"
(string/between "start:middle:end" "start:" ":end") ; => "middle"string/take
Take the first N characters (positive) or last N characters (negative).
(string/take "hello" 3) ; => "hel"
(string/take "hello" -2) ; => "lo"Prefix & Suffix
string/chop-start
Remove a prefix if present, otherwise return unchanged.
(string/chop-start "Hello World" "Hello ") ; => "World"
(string/chop-start "Hello" "Bye") ; => "Hello"string/chop-end
Remove a suffix if present.
(string/chop-end "file.txt" ".txt") ; => "file"
(string/chop-end "file.txt" ".md") ; => "file.txt"string/ensure-start
Ensure a string starts with a prefix (adds it if missing).
(string/ensure-start "/path" "/") ; => "/path"
(string/ensure-start "path" "/") ; => "/path"string/ensure-end
Ensure a string ends with a suffix.
(string/ensure-end "path" "/") ; => "path/"
(string/ensure-end "path/" "/") ; => "path/"string/wrap
Wrap a string with left and right delimiters.
(string/wrap "hello" "(" ")") ; => "(hello)"
(string/wrap "hello" "**") ; => "**hello**"string/unwrap
Remove surrounding delimiters if both present.
(string/unwrap "(hello)" "(" ")") ; => "hello"
(string/unwrap "hello" "(" ")") ; => "hello"Replacement
string/replace-first
Replace only the first occurrence of a substring.
(string/replace-first "aaa" "a" "b") ; => "baa"string/replace-last
Replace only the last occurrence.
(string/replace-last "aaa" "a" "b") ; => "aab"string/remove
Remove all occurrences of a substring.
(string/remove "hello world" "o") ; => "hell wrld"Case Conversion
string/snake-case
Convert to snake_case.
(string/snake-case "helloWorld") ; => "hello_world"
(string/snake-case "Hello World") ; => "hello_world"string/kebab-case
Convert to kebab-case.
(string/kebab-case "helloWorld") ; => "hello-world"
(string/kebab-case "Hello World") ; => "hello-world"string/camel-case
Convert to camelCase.
(string/camel-case "hello_world") ; => "helloWorld"
(string/camel-case "Hello World") ; => "helloWorld"string/pascal-case
Convert to PascalCase.
(string/pascal-case "hello_world") ; => "HelloWorld"
(string/pascal-case "hello world") ; => "HelloWorld"string/headline
Convert to Title Case headline.
(string/headline "hello_world") ; => "Hello World"
(string/headline "helloWorld") ; => "Hello World"string/words
Split a string into words (splits on non-alphanumeric boundaries).
(string/words "hello_world") ; => ("hello" "world")
(string/words "helloWorld") ; => ("hello" "World")
(string/words "Hello World!") ; => ("Hello" "World")