std.str
The complete UTF-8 string toolkit. Every operation is character-indexed, so it is correct for multi-byte text.
import std.str
str.title("the quick fox") # "The Quick Fox"
str.pad_start("7", 4, "0") # "0007"
str.substring("hello world", -5) # "world"
str.replace_first("a-b-c", "-", "/") # "a/b-c"
str.count("banana", "na") # 2
The most common operations are global built-ins already - upper, lower, trim, split, join, replace, contains, starts_with, ends_with, chars, lines, reverse, index_of, len. Import this module for the fuller set.
import std.strbindsstring, shadowing the globalstring()constructor for the rest of the scope. Usestring.from(x)to convert, orimport std.str as sto keepstring()free.
Case
upper, lower, capitalize (first letter up, rest down), title (each word), swapcase, and eq_ignore_case(a, b).
eq_ignore_case compares Unicode lowercased forms. It does not perform full case folding, so German ß does not compare equal to ss.
Trim
str.trim(s) # whitespace
str.trim(s, "x") # any character in the set
str.trim("xxhixx", "x") # "hi"
str.trim_prefix(s, "www.") # a fixed affix, once
str.trim_suffix(s, ".com")
trim, trim_start, trim_end take an optional character set, not a substring - trim(s, "ab") strips any a or b from the ends.
Pad
str.pad_start(s, 8) # fill defaults to a space
str.pad_end(s, 8, ".")
str.center(s, 20, "-") # odd leftover goes right
str.zfill("-5", 4) # "-005"
zfill pads with zeros after any leading sign, which is the one case pad_start(s, w, "0") gets wrong. A multi-character fill cycles and truncates to the gap.
Extract
str.substring(s, 1, 4) # clamps
str.substring(s, -5) # negative from the end
str.char_at(s, 0) # errors out of range
substring clamps; char_at errors. Same asymmetry as slices versus indexing.
Code points
str.ord("A") # 65
str.ord("𝄞") # 119070 - a non-BMP character
str.chr(65) # "A"
Scalar values, not bytes, so the pair round-trips for any character. ord("") errors; chr errors on a negative value, above U+10FFFF, or in the surrogate range.
Search
contains, starts_with, ends_with, index_of and last_index_of (character offset, or -1), count(s, sub) (non-overlapping).
Split and join
str.split("a,b,c", ",", 1) # ["a", "b,c"] limit caps the splits
str.rsplit("a,b,c", ",", 1) # ["a,b", "c"] counted from the right
str.partition("k=v=w", "=") # ["k", "=", "v=w"]
str.rpartition("k=v=w", "=") # ["k=v", "=", "w"]
str.split_whitespace(s)
str.lines(s)
str.join(xs, ", ")
partition and rpartition split once and always return a 3-list, so a destructure never fails:
let (key, sep, value) = str.partition(line, "=")
With no match, partition keeps the string in the head and rpartition in the tail.
Character-class tests
True only if the string is non-empty and every character is in the class: is_digit (Unicode numeric), is_alpha, is_alnum, is_space, is_ascii.
is_upper and is_lower need at least one cased character and no cased character of the opposite case - so "A1!" is upper, and "123" is neither.
The empty string is false for every predicate, one uniform rule. For "empty or whitespace" use string.is_blank.
Info and convert
len, is_empty, is_blank, repeat(s, n), from(x).
From bytes
str.from_utf8(b) # strict decode, catchable
str.from_utf8_lossy(b) # opt-in replacement characters
See Bytes.