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.
| Operator | Meaning |
|---|---|
+ | 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,charand enum values can be embedded. Acharprints as its UTF-8 text and an enum value as its member name ("facing ${dir}"givesfacing 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,1for1.0,0.30000000000000004for0.1 + 0.2,1e+21,NaN,Infinity,-Infinity. For a fixed number of decimals usefloat_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";.
| Method | Result | Description |
|---|---|---|
string.length() | infallible int | Byte count |
string.slice(offset, count) | string | Copy an exact byte range |
string.find(needle, start) | int | Byte offset of needle at or after start, or -1 |
string.ascii_lower() | infallible string | A copy with ASCII A through Z lowercased |
string.byte_at(index) | int | Unsigned byte value at index |
string.range_equals(offset, other, other_offset, count) | bool | Whether two byte ranges hold the same bytes |
string.to_buffer() | buffer | A 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
| Function | Result | Description |
|---|---|---|
int_to_string(value) | infallible string | Decimal digits, - for negative values |
float_to_string(value, decimals) | infallible string | Fixed-point with decimals fractional digits, clamped to 0..17; NaN, Infinity and -Infinity print as those words |
char_to_string(value) | infallible string | The character's UTF-8 text; '\0' gives "" |
int_from_string(text) | int | Parse a decimal integer |
float_from_string(text) | float | Parse 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.