Skip to main content

Strings

A string is immutable text, stored as bytes (normally UTF-8). A string can't be null and never contains a NUL byte. Strings pass freely between tasks and through channels.

string name = "cTurtle";
string greeting = "hello, " + name;

Operators​

Operators need no include.

OperatorMeaning
+Concatenation, producing a new string
==, !=Content equality
<, <=, >, >=Byte-wise content ordering

Concatenation can't fail, so it needs no ?. Comparison is always by content.

a + b copies both operands. Joining text piece by piece with + in a loop copies the accumulated prefix every iteration. Build with a buffer instead and convert once at the end.

Interpolation​

${expr} inside a string literal inserts the value of expr. It needs no include.

string label = "hull ${hull}/${hull_max}";
string status = "${name} at ${position_x}, ${position_y} (alive: ${alive})";
  • Strings, int, int32, byte, float, float32, bool, char and enum values can be embedded. A char prints as its UTF-8 text and an enum value as its member name ("facing ${dir}" gives facing North). Any other type is a compile error that names the type; convert it to a string first.
  • Floats print the shortest text that reads back as the same number: 0.1, 1 for 1.0, 0.30000000000000004 for 0.1 + 0.2, 1e+21, NaN, Infinity, -Infinity. For a fixed number of decimals use float_to_string.
  • The expression can contain anything, including braces and other strings: "total ${ {"a": 1}["a"]? }" and "<${ "[${n}]" }>" both work.
  • Write \$ for a literal $ before {. A $ followed by anything else is ordinary text.

The expression reference gives the exact float format.

Methods​

String methods and the functions below need include "string";.

MethodResultDescription
string.length()infallible intByte count
string.slice(offset, count)stringCopy an exact byte range
string.find(needle, start)intByte offset of needle at or after start, or -1
string.ascii_lower()infallible stringA copy with ASCII A through Z lowercased
string.byte_at(index)intUnsigned byte value at index
string.range_equals(offset, other, other_offset, count)boolWhether two byte ranges hold the same bytes
string.to_buffer()bufferA new buffer holding the string's bytes
string.chars()infallible array<char>The string's characters (Unicode code points) in a new array

slice, byte_at, and range_equals fail when a range or index lies outside the string. find fails when start is past the end. Offsets count bytes, as in buffer.slice, never code points.

Characters​

for walks a string one character (Unicode code point) at a time. It needs no include. The two-variable form also gives each character's position, counted in characters, not bytes:

int vowels = 0;
for (char c in name) {
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') vowels += 1;
}
for (int i, char c in "héllo") { /* i is 0, 1, 2, 3, 4 */ }

s.chars() returns the same characters as an array<char>. Bytes that are not valid UTF-8 read as '\u{FFFD}', one for each bad byte.

include "string";

fn domain(string address) -> string {
int at = address.find("@", 0)?;
if (at < 0) {
throw new error(1, "not an address: " + address);
}
return address.slice(at + 1, address.length() - at - 1)?;
}

Buffers and strings​

A buffer is the mutable builder for an immutable string:

include "buffer";
include "string";

fn shout(array<string> words) -> string {
buffer assembled = buffer_with_capacity(64)?;
for (string word in words) {
assembled.append_string(word)?;
assembled.append_string("! ")?;
}
return assembled.to_string()?;
}

buffer.to_string() copies the buffer's bytes into a new string. buffer.range_to_string(offset, count) copies only a range. Both fail if the bytes contain a NUL. string.to_buffer() goes the other way. Later writes to a buffer don't change a string already made from it. Collections and buffers lists the other conversions.

Numbers and strings​

FunctionResultDescription
int_to_string(value)infallible stringDecimal digits, - for negative values
float_to_string(value, decimals)infallible stringFixed-point with decimals fractional digits, clamped to 0..17; NaN, Infinity and -Infinity print as those words
char_to_string(value)infallible stringThe character's UTF-8 text; '\0' gives ""
int_from_string(text)intParse a decimal integer
float_from_string(text)floatParse a floating-point number

The parsers allow surrounding whitespace. They fail on an empty string, trailing characters, or an out-of-range value.

string digits = int_to_string(hull);
string ratio = float_to_string(hull_fraction, 2);
int count = int_from_string("42")?;

For mixed text and values, prefer interpolation. BT has no printf-style formatter.