std.string
String manipulation lives at std.string. A subset of these is also
available as self-operator pipes on strings (e.g. s:upper()) — the seeded
string pipes are upper lower trim trim_left trim_right substr replace replace_first repeat pad_left pad_right plus the dispatch pipes insert erase reverse. See Pipes for the full picture.
Reference
upper(s) lower(s) trim(s) trim_left(s) trim_right(s)
substr(s, start[, length]) char_at(s, index)
char_code(s[, index]) from_char_code(code...)
find(s, pattern) index_of(s, pattern) last_index_of(s, pattern)
contains(s, pattern) starts_with(s, prefix) ends_with(s, suffix)
count(s, pattern)
replace(s, old, new) replace_first(s, old, new)
repeat(s, n) reverse(s) insert(s, index, text) erase(s, start[, length])
pad_left(s, width[, fill]) pad_right(s, width[, fill])
split(s, delimiter[, limit]) join(array[, separator])
is_empty(s) is_alpha(s) is_digit(s) is_alnum(s)
is_upper(s) is_lower(s) is_space(s)
find / index_of / last_index_of return an index or -1 when not found.
count returns 0 when absent. All indices are 0-based; out-of-range
substr starts throw ARRAY_INDEX_OUT_OF_RANGE, while out-of-range
char_at throws too — check len() first when indexing untrusted input.
substr’s optional third argument is a length, not an end index; when
omitted it runs to the end of the string. Defaults: char_code(s) reads
index 0; join(array) separates with ""; pad_left/pad_right fill
with " "; split(s, delim) without limit splits fully, and with a limit
returns at most that many parts with the remainder in the last element.
Case and whitespace
print(std.string.upper("hello")) # HELLO
print(std.string.lower("HeLLo")) # hello
print(std.string.trim(" hi ")) # hi (both ends)
print(std.string.trim_left(" hi ")) # "hi " (leading only)
print(std.string.trim_right(" hi ")) # " hi" (trailing only)
# they are pipes too, and chain:
var s = " hello "
print(s:trim():upper()) # HELLO
The whole trim family is commonly used to clean up captured command output —
e.g. std.string.trim(std.system("whoami").output) to strip its trailing
newline.
Searching
All searches are plain substring searches (not regular expressions):
var s = "banana"
std.string.contains(s, "na") # true
std.string.starts_with(s, "ban") # true
std.string.ends_with(s, "ana") # true
std.string.count(s, "na") # 2
std.string.find(s, "na") # 2 (first index)
std.string.index_of(s, "na") # 2 (same)
std.string.last_index_of(s, "na") # 4 (search from the end)
std.string.find(s, "zz") # -1 (not found)
Picking characters and codes
var word = "abc"
std.string.char_at(word, 1) # "b" (1-char string)
std.string.char_code(word) # 97 (first char)
std.string.char_code(word, 1) # 98 (char at index 1)
std.string.from_char_code(72, 105) # "Hi" (codes -> string)
from_char_code accepts any number of codes, so it pairs neatly with char_code
for character arithmetic, e.g. shifting letters, or producing a string from
returned byte codes.
Cutting, joining, and editing
substr extracts a run (length, not end index). replace swaps every
occurrence; replace_first only the first:
var s = "a-b-c"
std.string.substr(s, 1, 3) # "-b-" (3 chars from index 1)
std.string.substr(s, 4) # "c" (to the end)
std.string.replace(s, "-", "+") # "a+b+c"
std.string.replace_first(s, "-", "+") # "a+b-c"
std.string.insert("abc", 1, "XY") # "aXYbc"
std.string.erase("hello", 1, 2) # "hlo" (2 chars from index 1)
std.string.reverse("abc") # "cba"
std.string.repeat("ab", 3) # "ababab"
Splitting and building
std.string.split("a,b,c", ",") # ["a", "b", "c"]
std.string.split("a,b,c,d", ",", 2) # ["a", "b,c,d"] (cap at 2)
std.string.join(["a", "b", "c"]) # "abc" (no separator)
std.string.join(["a", "b"], "-") # "a-b"
# trim + split is the classic CSV / config-line idiom
var line = " name, age, city "
var parts = std.string.split(std.string.trim(line), ",")
print(parts) # ["name", " age", " city"]
std.string.pad_left("7", 3, "0") # "007"
std.string.pad_right("x", 3, ".") # "x.."
Classification
Each is_* test returns a boolean:
std.string.is_digit("5") # true
std.string.is_alpha("a") # true
std.string.is_alnum("a1") # true
std.string.is_space(" ") # true (newline/tab etc. too)
std.string.is_upper("A") # true
std.string.is_lower("a") # true
std.string.is_empty("") # true
Use split plus every from std.sort to check an entire string:
# is every character a digit?
var pin = "1234"
var chars = std.string.split(pin, "") # ["1", "2", "3", "4"]
print(std.sort.every(chars, fn(c) { return std.string.is_digit(c) })) # true