Summary#
Text.TrimStart(s) and Text.TrimEnd(s) remove whitespace from one end of a string (Text.Trim(s) does
both). Give any of the three a second argument and it removes those CHARACTERS instead — C#'s
TrimEnd(params char[]), with the set written as a string because Osy# has no char type. Text.PadStart(s, width, pad) and Text.PadEnd(s, width, pad) grow a string to at least width
characters by adding a fill on the left or right. Padding never shortens a string, and the fill is a
string, not a single character.
Signature#
Text.TrimStart(<string> s) -> string // strip leading whitespace
Text.TrimEnd(<string> s) -> string // strip trailing whitespace
Text.TrimStart(<string> s, <string> chars) -> string // …or strip any of THESE characters
Text.TrimEnd(<string> s, <string> chars) -> string
Text.Trim(<string> s, <string> chars) -> string // both ends
Text.PadStart(<string> s, <int> width, <string> pad) -> string // fill on the LEFT to `width`
Text.PadEnd(<string> s, <int> width, <string> pad) -> string // fill on the RIGHT to `width`Description#
Trimming characters rather than whitespace#
The second argument is a SET, not a suffix: every character in it is stripped from that end, repeatedly, exactly as
C#'s char[] overload does. So TrimEnd("/") removes as many trailing slashes as there are, and TrimEnd("/\\")
removes either kind.
string JoinUrl(string baseUrl, string path) {
return Text.TrimEnd(baseUrl, "/") + "/" + Text.TrimStart(path, "/");
}⚠ It is a set of characters, so TrimEnd(url, "/api") strips any trailing /, a, p or i — not the word
"api". That is C#'s behaviour too, and it is the one thing about these overloads that surprises people.
Trimming uses the full Unicode whitespace set#
TrimStart/TrimEnd strip every leading/trailing whitespace character, not just the ASCII space — a tab, a
newline, a no-break space ( ), an ideographic space ( ) are all removed. Only the named end is touched:
Text.TrimStart(" hi ") is "hi " (trailing spaces survive).
Padding never truncates, and takes a WIDTH not a count-to-add#
width is the target length, not "how many characters to add". If the string is already at least that
long it comes back unchanged: Text.PadStart("hello", 3, "0") is "hello". Otherwise the gap is filled
to reach exactly width.
The pad is a STRING, cut to fit the gap#
The fill is repeated and then cut to exactly the deficit, so a multi-character pad can end mid-repeat:
Text.PadStart("7", 3, "ab") fills two characters — "ab7" — and Text.PadEnd("7", 4, "ab") is "7aba".
An empty pad is a no-op (it cannot fill anything), so Text.PadStart("x", 5, "") is "x" — it does not
loop forever. For zero-padding a number, a format specifier like D5 or "00000" is usually clearer than
padding a string — see format specifiers.
All four run in memory on a value already in hand.
Examples#
// Trim stray whitespace, then right-justify into a fixed-width column with leading zeros.
string Ticket(string code) {
return Text.PadStart(Text.TrimEnd(Text.TrimStart(code)), 6, "0");
}[Test]
void Text_affix_answers() {
Assert.Equal("000042", Ticket(" 42 "));
Assert.Equal("hi ", Text.TrimStart(" hi ")); // only the leading end
Assert.Equal(" hi", Text.TrimEnd(" hi "));
Assert.Equal("hi", Text.TrimStart(" hi")); // an ideographic space is whitespace too
Assert.Equal("007", Text.PadStart("7", 3, "0"));
Assert.Equal("ab7", Text.PadStart("7", 3, "ab")); // multi-char pad, cut to the 2-char gap
Assert.Equal("hello", Text.PadStart("hello", 3, "0")); // already long enough → unchanged
Assert.Equal("x", Text.PadStart("x", 5, "")); // empty pad → no-op, never an infinite loop
Assert.Equal("700", Text.PadEnd("7", 3, "0"));
Assert.Equal("7aba", Text.PadEnd("7", 4, "ab"));
}See also#
- Text.Length, Text.IsEmpty, Text.IsBlank, Text.Contains, Text.StartsWith, Text.EndsWith —
Text.Length, and the emptiness checks - format specifiers — zero-padding a number (
D5,"00000"), usually better than padding a string - Text.TitleCase — the other in-memory string builtins