Skip to content

UXText

UXText provides the common string operations that the base String does not: trimming, splitting, joining, case folding, prefix/suffix/contains, and single-character replacement.

Every method is static, and every result is a fresh buffer. Nothing is mutated in place, so the methods are safe to call on a literal, on a field, or on a pointer that other code still holds.

#use <UXKit> // or #import "UXText.xc"
UXText.trimWhitespace((u8*)" hello world \n"); // "hello world"
UXText.toUpper((u8*)"Grand Bleu"); // "GRAND BLEU"
UXText.hasSuffix((u8*)"system.fnt", (u8*)".fnt"); // true
Array<UXStrItem>* parts = UXText.split((u8*)"a,b,c", (u8)',');
UXText.partAt(parts, 1); // "b"
UXText.join(parts, (u8*)" | "); // "a | b | c"

Split results come back as an Array of UXStrItem. The box is needed because an array holds objects and a bare u8* is not one. partAt unwraps a part, so you rarely name the box.

The two methods give different answers, and each is correct for a different job.

UXText.split((u8*)"a,,b", (u8)','); // 3: "a" "" "b"
UXText.split((u8*)"a,b,", (u8)','); // 3: "a" "b" ""
UXText.split((u8*)"", (u8)','); // 1: ""

split is structural: a delimiter marks a field boundary, so an empty field is a field. A CSV row needs this: a blank cell is a cell, and a trailing comma means a trailing empty column.

Split and join also round-trip: join(split(s, ','), ",") returns s unchanged, because the empty fields are kept.

UXText.tokenize((u8*)" ls -l /usr ",
UXCharacterSet.whitespaceAndNewlines()); // 3: "ls" "-l" "/usr"

tokenize is lexical: a run of delimiters is one gap, and leading and trailing delimiters produce nothing. Use it to split a command line, a sentence into words, or a space-separated attribute.

The delimiter also differs. split takes one byte; tokenize takes a UXCharacterSet, so “break on comma, semicolon or tab” is one set and one pass.

trim also takes a set, so it can trim more than whitespace:

UXCharacterSet* quotes = new UXCharacterSet();
quotes.addString((u8*)"\"'");
UXText.trim((u8*)"\"quoted\"", quotes); // quoted

trimWhitespace covers the common case, such as a line read from a file.

These methods work on bytes. slen is a byte count, split takes a byte, and toLower folds AZ only; accented letters are left alone.

UTF-8 is self-synchronizing, which limits the effect of this:

UXText.contains((u8*)"abc", (u8*)""); // true — everything contains nothing
UXText.hasPrefix((u8*)"abc", (u8*)""); // true
UXText.hasPrefix((u8*)"ab", (u8*)"abcdef"); // false — stops at the NUL

The empty-needle answers match NSString. They let an empty search box match everything without a special case.

A prefix longer than the string is safe: the comparison reaches the haystack’s NUL and fails there without reading past it.

trim · trimWhitespace · split · tokenize · partAt · join · toLower / toUpper · hasPrefix · hasSuffix · contains · replaceChar · slen · dup

static u8* trim(u8* s, UXCharacterSet* cs)

Removes leading and trailing characters that are in the set. A string made entirely of delimiters becomes "".

static u8* trimWhitespace(u8* s)

trim with whitespaceAndNewlines.

static Array<UXStrItem>* split(u8* s, u8 delim)

Splits on one byte, keeping empty fields. Always returns at least one part.

static Array<UXStrItem>* tokenize(u8* s, UXCharacterSet* sep)

Splits on any character in the set, dropping empty fields. A string made entirely of delimiters gives an empty array.

static u8* partAt(Array<UXStrItem>* parts, i32 i)

Unwraps one part, so you do not need to name UXStrItem.

static u8* join(Array<UXStrItem>* parts, u8* sep)

Concatenates with a separator between parts, with none leading or trailing. An empty array gives "".

static u8* toLower(u8* s)
static u8* toUpper(u8* s)

ASCII case folding. See above.

static bool hasPrefix(u8* s, u8* p)
static bool hasSuffix(u8* s, u8* suf)

The extension test. False when the suffix is longer than the string.

static bool contains(u8* hay, u8* needle)

Substring search. True for an empty needle. See the caution on cost.

static u8* replaceChar(u8* s, u8 from, u8 to)

Replaces every occurrence of one byte with another, in a new buffer. The length is preserved, so this cannot replace a character with a string; use split and join for that.

static i32 slen(u8* s)

Byte length. Null-safe: returns 0 for null, so the other methods do not each need a null guard.

static u8* dup(u8* s, i32 start, i32 len)

A fresh NUL-terminated copy of a range. A negative length is clamped to zero.

trim: 'hello world'
unquote: quoted
split a,,b -> 3: [a] [] [b]
split a,b, -> 3: [a] [b] []
split '' -> 1: []
tokenize -> 3: [ls] [-l] [/usr]
join: a | | b
round trip: a,b,
prefix=1 suffix=1 contains=1
empty needle=1 empty prefix=1
long prefix=0
case: GRAND BLEU grand bleu

The program is website/site/examples/uxkit/strings.xc. The doc-examples gate compiles it, and the output above is what it prints.

  • A plain class (not an Object subclass). All methods are static, so there is nothing to instantiate.
  • UXStr: concatenation and number conversion
  • UXStrItem: the box split results come in
  • UXCharacterSet: the delimiter sets
  • UXCSV: when the fields are quoted and split is not enough