# Strings
String operations are bare stdlib verbs available in every runtime. Indexing
is codepoint-based: `len` counts characters, and `$s(a..b)` slicing,
`nth`, `char-at`, and `index-of` all operate on whole characters — slicing
can never land mid-character. Internally strings are UTF-8 byte arrays and
binary-safe; the `bytes/*` verbs give raw access for binary data. Display
width (East Asian Wide glyphs take 2 terminal cells) is a separate concern:
use the width verbs, not `len`, whenever you position text in a terminal
grid. Full story: [String
Representation](../10-System-Design/string-representation).
## Functions
Core string operations:
| Function | Purpose |
|---|---|
| `str $xs...` | Concatenate values to a string |
| `$s << x` | Concatenate onto a string local in place (sugar for `set s [str $s x]`) |
| `append s v` | New string with `v` concatenated (pure — also works on nil) |
| `format fmt args...` | Printf-style formatting (`%d %x %f %s ...` with flags/width/precision) |
| `upper s`, `lower s` | Case conversion |
| `trim s` | Strip leading and trailing whitespace |
| `split s sep` | Split into a list (empty `sep` splits into characters) |
| `join xs sep` | Join a list into a string |
| `replace s old new` | Replace all occurrences |
| `replace-first s old new` | Replace the first occurrence only |
| `starts-with? s prefix`, `ends-with? s suffix` | Prefix / suffix predicates |
| `repeat s n` | Repeat the string `n` times |
| `lines s` | Split on newlines |
| `words s` | Split on whitespace |
| `contains s substr` | Substring check |
| `reverse s` | Reverse by codepoint (multi-byte characters stay intact) |
| `char-at s i` | Character at a codepoint index (nil out of range) |
| `index-of s sub` | Find a substring at a codepoint index, or -1 |
| `chars/count s`, `chars/slice s a b` | Explicit character count / range |
Layout (display width) — use these, not `len`, when positioning text in
a terminal-cell UI:
| Function | Purpose |
|---|---|
| `width s` | Display width in cells (East Asian Wide = 2) |
| `cells s` | Per-character `@{char-index char-string width}` triples |
| `truncate-width s w ?tail` | Longest prefix fitting `w` cells (optional "…" tail) |
| `pad-width-left/right s w ?fill` | Pad to a display width |
| `wrap-width s w` | Word wrap at display width into a list of lines |
| `pad-left s n ch`, `pad-right s n ch` | Pad to a character count |
Bytes (binary data) — for `fs/read` bodies, crypto, and protocol
offsets, where chunk boundaries must not drift:
| Function | Purpose |
|---|---|
| `bytes/len s` | Byte length |
| `bytes/slice s a b` | Raw byte range |
| `byte-at s i` | Byte value (0-255) |
The codepoint verbs are the text layer: `char-at` and `index-of` never
return a raw byte fragment, and `split` with an empty separator yields whole
characters. The byte verbs are the data layer: use them for encoded blobs,
protocol offsets, and any body that merely happens to contain UTF-8 runs.
`width` measures terminal cells; invalid UTF-8 bytes count 1 each. For
character classification (`chars/alpha?`, `chars/digit?`, ...) see
Chars; for pattern matching see Regex.
## Examples
### Codepoints vs bytes vs display cells
set s "a中c" echo "chars:" $s(len) # 3 echo "bytes:" [bytes/len $s] # 5 echo "cells:" [width $s] # 4
### Core operations
set parts [split "a,b,c" ","] echo [join $parts " | "] # a | b | c echo [upper [trim " hi "]] # HI echo [replace "foo bar foo" "foo" "baz"] echo [char-at "a中c" 1] # 中 echo [index-of "a中b文c" "文"] # 3 echo [repeat "=" 20]
### Building strings
For one-shot concatenation use `str` (or interpolation). To build a
string across a loop, accumulate with `<<` — it renders values exactly
like `str` and reuses one buffer, so the loop stays linear:
set s "count: " $s << 42 # count: 42 — any value str-ifies $s << " items" # count: 42 items def render-rows {|rows| # nil starts an empty string set out nil for r in $rows { $out << [str "|" $r~name "|" $r~qty "\n"] } $out } [append "a" "b"] # => "ab" (pure — new string) [append nil "b"] # => "b"
For joining an existing list, `join` is still the right verb:
`[join @{a b c} "-"]` → `"a-b-c"`. Keep hot `<<` loops inside a
function — the local then gets the fast in-place path (a global
receiver re-copies the string each time). See
Syntax and Style.
### Layout: width-aware padding, truncation, wrapping
echo [pad-width-right "中文" 8 "-"] # 中文---- echo [truncate-width "hello world" 8 "…"] # hello w… for line [wrap-width "the quick brown fox" 9] { echo $line }
### Bytes: raw access for binary data
set blob [bytes/slice "a中c" 0 3] # 3 raw bytes echo [byte-at "a中c" 1] # 228 (0xE4, lead byte of 中)