Class string

extends Object implements Comparable Data
A Unicode text object.
Strings are sequences of Unicode code points, stored in UTF-8 format.
This is a fully fledged class, not a 'primitive type'.
A string can only contain valid UTF-8 byte sequences. To store arbitrary byte sequences or other encodings like ISO 8859, use ByteArray.
Strings are immutable objects.

Class summary


from-rune rune/int -> string
Constructs a single-character string from one Unicode code point.
from-runes runes/List -> string
Constructs a string from a list of Unicode code points.
format format/string object/any -> string
Formats the object according to the given format.
from-utf-16 byte-array/ByteArray -> string
Treats the byte array as little endian UTF-16 and converts it to a string.

Statics

format format/string object/any -> string
Formats the object according to the given format.
The normal way of using this functionality is through the string interpolation syntax - see https://docs.toit.io/language/strings/#string-interpolation.
The format description is very similar to printf.
Extensions relative to printf:
  • ^ for centering.
  • 'b' for binary.
Missing relative to printf: No support for %g or %p.
The %u type treats integer as unsigned 64 bit integers.
Like in printf the hexadecimal and octal format specifiers, %x and %o will treat all values as unsigned. This also applies to the binary format specifier, %b. See also int.stringify.
Format Description:

[alignment][precision][type]
alignment = flags<digits>
flags = '-' | '^' | '>'   (> is default, can't be used in the syntax)
precision = .<digits>
type 'd' | 'f' | 's' | 'o' | 'x' | 'c' | 'b' | 'u'

Constructs a single-character string from one Unicode code point.
If rune is greater than 0x7f, the result string will have size > 1 and contain more than one UTF-8 byte .
Examples

str1 := string.from-rune 'a'  // -> "a"
str2 := string.from-rune 0x41 // -> "A"
str3 := string.from-rune 42   // -> "*"
str4 := string.from-rune 7931 // -> "☃"

Constructs a string from a list of Unicode code points.
All elements of the list must be in the Unicode range of 0 to 0x10ffff, inclusive.
Since UTF-8 encoding is used, if any elements of the list are greater than the maximum ASCII value of 0x7f the size of the string will be greater than the size of the list.
UTF-8 bytes are not valid input to this constructor. If you have a ByteArray of UTF-8 bytes, use the ByteArray.to-string method instead.
Examples

str1 := string.from-runes ['a', 'b', 42]  // -> "ab*"
str2 := string.from-runes [0x41]          // -> "A"
str3 := string.from-runes [42]            // -> "*"
str4 := string.from-runes [7931, 0x20ac]  // -> "☃€"

Treats the byte array as little endian UTF-16 and converts it to a string.
If the byte array is not a valid UTF-16 string, error characters (U+FFFD) are inserted as replacements. Unpaired surrogates are considered invalid and replaced with the error character.

Methods

at --raw/True i/int -> int
The raw byte (Unicode codeunit) at position i in the UTF-8 byte representation of this string.
Contrary to at this method never returns null.

byte-at index/int -> int

byte-slice from/any to/int -> Data

Compares the two given strings.
Returns 1 if this instance is greater than other.
Returns 0 if this instance is equal to other.
Returns -1 if this instance is less than other.
The comparison is done based on Unicode values. That is, string A is considered less than string B, if a leading prefix (potentially empty) is the same, and string A has a Unicode value (rune/code unit) less than string B at the following position.
If string A is a prefix of string B, then A is less than B.
Errors
Since natural languages often have different requirements for sorting, it is not sufficient to use this method for natural language sorting (also known as "collation").
For example, this method considers "Amélie" as greater than "Amzlie". In French, accented characters should be ordered similar to non-accented characters. This ordering would thus be wrong.
Similarly, in Spanish, words containing "ñ" would not be sorted correctly. The "ñ" is collated between "n" and "o" (contrary to Unicode's position after all ASCII characters).
Examples

"a".compare-to "b"    // => -1
"a".compare-to "a"    // => 0
"b".compare-to "a"    // => 1
"ab".compare-to "abc" // => -1
"abc".compare-to "ab" // => 1
"Amélie".compare-to "Amelie"  // => 1
"Amélie".compare-to "Amzlie"  // => 1

compare-to other/string [--if-equal] -> int
Compares this instance with other and calls if-equal if the two are equal.
See compare-to for documentation on the ordering.
The if-equal block is called only if this instance and other are equal.
The if-equal block should return -1, 0, or 1 (since it becomes the result of the call to this method).
Examples
The if-equal block allows easy chaining of compare-to calls.

// In class A with fields str-field1 and str-field2:
compare-to other/A -> int:
  return str-field1.compare-to other.str-field1 --if-equal=:
    str-field2.compare-to other.str-field2

contains needle/string from/int=0 to/int=size -> bool
Returns whether needle is present in this instance.
The optional parameters from and to delimit the range in which the needle is searched in. The needle must be fully contained in the range from..to (as if taking a copy with these parameters) to return true.
The range from - to must be valid and satisfy 0 <= from <= to <= size.

Returns true iff the string has no non-ASCII characters in it.
The implementation is optimized, but it takes linear time in the size of the string.

copy from/int=0 -> string
Copies the string between from (inclusive) and size (exclusive).
The given substring must be legal. That is, from must not point into the middle of a multi-byte sequence.

copy from/int to/int -> string
Copies the string between from (inclusive) and to (exclusive).
The given substring must be legal. That is, neither from nor to can point into the middle of a multi-byte sequence.

copy from/int to/int=size --force-valid/bool -> string
Copies the string between from (inclusive) and to (exclusive).
If force-valid is true, adjusts from and to so that they are valid substring indexes.
If from (resp. to) points to the middle of a multi-byte sequence decreases the index until it points to the beginning of the sequence.
Also see rune-index.

do [block] -> none
Iterates over all slots in the string (as if using at) and calls the given block with the values.
For every multi-byte sequences in the string, the block is invoked first with the rune (Unicode "code point"), then with null for each remaining codeunit of the sequence.
This function is equivalent to:

size.repeat: block.call this[it]
Examples

"é".do: print it // 233, null

do --runes/True [block] -> none
Iterates over all runes (Unicode "code point") and calls the given block with the values.
Contrary to do, only invokes block with valid integer values. For every multi-byte sequences there is only one call to block.
Examples

"Amélie".do --runes: print "$(%c it)" // A, m, é, l, i, e

Whether this instance ends with the given suffix.

flat-map [block] -> string
Calls the given block for every unicode character in the string.
The argument to the block is an integer in the Unicode range of 0-0x10ffff, inclusive.
The return value is assembled from the return values of the block.
If a string is returned from the block it is inserted at that point in the return value.
If a byte array is returned from the block it is converted to a string and treated like a string. The byte array must contain whole, valid UTF-8 sequences.
If an integer is returned from the block it is treated as a Unicode code point, and the corresponding code point is inserted.
If the block returns null, this is treated like the zero length string.
If the block returns a list, then every element in the list is handled like the above actions, but this is only done for one level - lists of lists are not flattened in this way.
To get a list or byte array as the return value instead of a string, use str.to-byte-array.map instead.
Examples.

heavy-metalize str/string -> string:
  return str.flat_map: | c |
    {'o': 'ö', 'a': 'ä', 'u': 'ü', 'ä': "\u{20db}a"}.get c --if-absent=: c

lower-case str/string -> string:
  return str.flat-map: | c | ('A' <= c <= 'Z') ? c - 'A' + 'a' : c

glob pattern/string -> bool
Whether this instance matches a simplified glob pattern.
Two characters are used for wildcard matching: '?' will match any single Unicode character. '*' will match any number of Unicode characters.
Examples

"Toad".glob "Toad"   // => true
"Toad".glob "To?d"   // => true
"Toad".glob "To"     // => false
"To*d".glob "To\\*d" // => true
"Toad".glob "To\\*d" // => false

hash-code -> any
The hash code for this instance.
This operation is in O(1).

index-of --last/bool=false needle/string from/int=0 to/int=size -> int
Searches for needle in the range from (inclusive) - to (exclusive).
If last is false (the default) returns the first occurrence of needle in the given range from - to.
If last is true returns the last occurrence of needle in the given range from - to, by searching backward. The needle must be entirely contained within the range.
The optional parameters from and to delimit the range in which the needle is searched in. The needle must be fully contained in the range from..to (as if taking a copy with these parameters) to find the needle.
The range from - to must be valid and satisfy 0 <= from <= to <= size.
Returns -1 if needle is not found.
Examples

"foobar".index-of "foo"  // => 0
"foobar".index-of "bar"  // => 3
"foo".index-of "bar"     // => -1

"foobarfoo".index-of "foo"           // => 0
"foobarfoo".index-of "foo" 1         // => 6
"foobarfoo".index-of "foo" 1 8       // => -1

// Invalid ranges:
"foobarfoo".index-of "foo" -1 999    // Throws.
"foobarfoo".index-of "foo" 1 999     // Throws.

"".index-of "" 0 0   // => 0
"".index-of "" -3 -3 // => Throws
"".index-of "" 2 2   // => Throws

// Last:
"foobarfoo".index-of --last "foo"           // => 6
"foobarfoo".index-of --last "foo" 1         // => 6
"foobarfoo".index-of --last "foo" 1 6       // => 0
"foobarfoo".index-of --last "foo" 0 1       // => 0
"foobarfoo".index-of --last "foo" 0 8       // => 0

"foobarfoo".index-of --last "gee"           // => -1
"foobarfoo".index-of --last "foo" 1 5       // => -1
"foobarfoo".index-of --last "foo" 0 8       // => 0

index-of --last/bool=false needle/string from/int=0 to/int=size [--if-absent] -> any
Searches for needle in the range from (inclusive) - to (exclusive).
If last is false (the default) returns the first occurrence of needle in the given range from - to.
If last is true returns the last occurrence of needle in the given range from - to, by searching backward.
The optional parameters from and to delimit the range in which the needle is searched in. The needle must be fully contained in the range from..to (as if taking a copy with these parameters) to find the needle.
The range from - to must be valid and satisfy 0 <= from <= to <= size.
Calls if-absent with this instance if needle is not found, and returns the result of that call.
Examples
Also see index-of for more examples.

"foo".index_of "bar" --if-absent=: it.size            // => 3 (the size of "foo")
"foobarfoo".index_of "foo" 1 8 --if-absent=: 499      // => 499
"".index_of "" -3 -3 --if-absent=: throw "not found"  // Error
"".index_of "" 2 2   --if-absent=: -1                 // => -1
"foobarfoo".index_of "foo" 1 8 --if-absent=: 42       // => 42

Whether this instance is the empty string "".

matches needle/string --at/int -> bool
Whether this instance has an occurrence of needle at index at.
The index at does not need to be valid.
Examples

"Toad the Wet Sprocket".matches "Toad"     --at=0   // => true
"Toad the Wet Sprocket".matches "Toad"     --at=-1  // => false
"Toad the Wet Sprocket".matches "Sprocket" --at=13  // => true

operator [..] --from/any=0 --to/any=size -> string
Returns a slice of this string.
Slices are views on the underlying object. Contrary to copy, they don't (always) create a new string, but rather point into the original string.
String slices behave exactly the same as normal strings.
The parameter from is inclusive.
The parameter to is exclusive.
Advanced
Slices keep the whole string alive. This can lead to memory waste if the string is not used otherwise. In some cases it might thus make sense to call copy on the slice.
At the call-site the arguments from and to are passed in with the slice syntax: str[from..to]. Since both arguments are optional (as they have default values), it is valid to omit from or to.
Positions that would create an invalid UTF-8 sequence are rejected with an exception.
Examples

str := "Hello, world!"
hello := str[..5]
world := str[7..]
comma := str[5..6]
print hello  // => "Hello"
print comma  // => ","
print world  // => "world!"
amelie := "Amélie"
amelie[2..3]  // Throws an exception.

The rune (Unicode "code point") at position i of the underlying bytes.
Returns null if i points into the middle of a multi-byte sequence.
It is an error if i is not in range 0 (inclusive) to size (exclusive).
Examples

str := "Amélie"
print "$(%c str[2])" // => é
print str[3]         // => null
print "$(%c str[4])" // => l

Concatenates amount copies of this instance.
The parameter amount must be >= 0.

Concatenates this instance with the given other string.

Whether this instance is less than other.
Uses compare-to to determine the ordering of the two strings.

Whether this instance is less or equal to other.
Uses compare-to to determine the ordering of the two strings.

operator == other/any -> bool
See super.

Whether this instance is greater than other.
Uses compare-to to determine the ordering of the two strings.

Whether this instance is greater or equal to other.
Uses compare-to to determine the ordering of the two strings.

pad --left/True=true amount/int char/int=' ' -> string
Pads this instance with char on the left, until the total size of the string is amount.
Returns this instance directly if this instance is longer than amount.
Examples

str := "foo"
str.pad --left 5     // => "  foo"
str.pad --left 5 '0' // => "00foo"

str.pad --left 3     // => "foo"
str.pad --left 1     // => "foo"

str.pad 5     // => "  foo"
str.pad 5 '0' // => "00foo"

str.pad 3     // => "foo"
str.pad 1     // => "foo"

pad --right/True amount/int char/int=' ' -> string
Pads this instance with char on the right, until the total size of the string is amount.
Returns this instance directly if this instance is longer than amount.
Examples

str := "foo"
str.pad --right 5     // => "foo  "
str.pad --right 5 '0' // => "foo00"

str.pad --right 3     // => "foo"
str.pad --right 1     // => "foo"

pad --center/True amount/int char/int=' ' -> string
Pads this instance with char on the left and right, until the total size of the string is amount.
Returns a string where this instance is centered. If the padding can't be divided evenly, adds more padding to the right.
Returns this instance directly if this instance is longer than amount.
Examples

str := "foo"
str.pad --center 5     // => " foo "
str.pad --center 5 '0' // => "0foo0"

str.pad --center 6     // => " foo  "
str.pad --center 6 '0' // => "0foo00"

str.pad --center 3     // => "foo"
str.pad --center 1     // => "foo"

replace --all/bool=false needle/string replacement/string from/int=0 to/int=size -> string
Replaces the given needle with the replacement string.
If all is true, replaces all occurrences of needle. Otherwise, only replaces the first occurrence.
Does nothing, if this instance doesn't contain the needle.
This operation only replaces occurrences of needle that are fully contained in from-to.

replace --all/bool=false needle/string from/int=0 to/int=size [replacement-callback] -> string
Replaces the given needle with the result of calling replacement-callback.
If all is true, replaces all occurrences of needle. For each found occurrence calls the replacement-callback with the matched string as argument.
If all is false (the default), only replaces the first occurrence with the result of calling replacement-callback with the matched string.
Does nothing, if this instance doesn't contain the needle.
This operation only replaces occurrences of needle that are fully contained in from-to.

rune-index index/int -> int
Returns the index of the rune pointed to by index.
Returns index if it is equal to the size.
Returns index if it points to the beginning of a rune.
Otherwise decreases index until it points to the beginning of the multi-byte sequence.
The parameter index must satisfy: 0 <= index <= size.

The size of this instance in UTF-8 code units (byte-sized).
The string may have fewer runes (Unicode "code points") than its size.
For example the string "Amélie" has a size of 7, but a size --runes of 6.

size --runes/True -> int
Returns the number of runes (Unicode "code points") in this string.
This operation takes linear time to complete as it runs through the whole string.

split --at-first/bool=false separator/string --drop-empty/bool=false [process-part] -> none
Splits this instance at separator.
If at-first is false (the default) splits at *every* occurrence of separator.
If at-first is true, splits only at the first occurrence of separator.
If drop-empty is true, then empty strings are ignored and silently dropped. This happens before a call to process-part.
Calls process-part for each part. It drop-empty is false and this instance starts or ends with a separator, then process-part is invoked with the empty string first and last, respectively.
Splits are never in the middle of a UTF-8 multi-byte sequence. This is normally a consequence of the seperator (as well as this instance) being well-formed UTF-8. However, it is explicitly enforced for the zero length separator (the empty string).
As a special case the empty separator does not result in a zero length string as the first and last entries, even though the empty separator can be found at both ends. However if at-first is true and the separator is empty then the result is one character, followed by the rest of the string even if that is an empty string.
Examples

"Toad the Wet Sprocket".split "e": print it  // prints "Toad th", " W", "t Sprock", and "t"
" the dust ".split " ": print it             // prints "the", "dust", and ""
"abc".split  "":    print it                 // prints "a", "b", and "c"
"foo".split  "foo": print it                 // prints "" and ""
"afoo".split "foo": print it                 // prints "a" and ""
"foob".split "foo": print it                 // prints "" and "b"
"".split "": print it                        // Doesn't print.

gadsby := "If youth, throughout all history, had had a champion to stand up for it;"
gadsby.split "e": print it // prints the contents of gadsby

"Toad the Wet Sprocket".split --at-first "e": print it  // prints "Toad th", " Wet Sprocket"
" the dust ".split            --at-first " ": print it  // prints "", "the dust "
gadsby.split                  --at-first "e": print it  // prints the contents of gadsby

"abc".split  --at-first "":    print it     // prints "a" and "bc"
"foo".split  --at-first "foo": print it     // prints "" and ""
"afoo".split --at-first "foo": print it     // prints "a" and ""
"foob".split --at-first "foo": print it     // prints "" and "b"
"".split     --at-first "":    print it     // This is an error.
"a".split    --at-first "":    print it     // prints "a" and ""

"foo".split "foo" --drop-empty: print it                 // Doesn't print.
"afoo".split "foo" --drop-empty: print it                 // prints "a"

split --at-first/bool=false separator/string --drop-empty/bool=false -> List
Splits this instance at separator.
Returns a list of the separated parts.
If at-first is false (the default) splits at *every* occurrence of separator.
If at-first is true, splits only at the first occurrence of separator.
If drop-empty is true, then empty strings are not included in the result.
Splits are never in the middle of a UTF-8 multi-byte sequence. This is normally a consequence of the seperator (as well as this instance) being well-formed UTF-8. However, it is explicitly enforced for the zero length separator (the empty string).
Examples

"Toad the Wet Sprocket".split "e"  // => ["Toad th", " W", "t Sprock", "t"]
" the dust ".split " "             // => ["", "the", "dust", ""]
"abc".split  ""                    // => ["", "a", "b", "c"]
"foo".split  "foo"                 // => ["", ""]
"afoo".split "foo"                 // => ["a", ""]
"foob".split "foo"                 // => ["", "b"]
"".split ""                        // => [""]

gadsby := "If youth, throughout all history, had had a champion to stand up for it;"
gadsby.split "e"   // => [gadsby]

"Toad the Wet Sprocket".split --at-first "e"  // => ["Toad th", " Wet Sprocket"]
" the dust ".split            --at-first " "  // => ["", "the dust "]
gadsby.split                  --at-first "e"  // => [gadsby]

"abc".split  --at-first ""      // => ["", "abc"]
"foo".split  --at-first "foo"   // => ["", ""]
"afoo".split --at-first "foo"   // => ["a", ""]
"foob".split --at-first "foo"   // => ["", "b"]
"".split     --at-first ""      // => [""]

Whether this instance starts with the given prefix.

stringify -> any

substitute [block] --open/string="{{" --close/string="}}" -> string
Replaces variables in a string with their values.
The input is searched for variables, which are arbitrary text surrounded by the delimiters, open and close.
By default it uses double braces, looking for {{variable}}.
The variable names (with whitespace trimmed) are passed to the block and the return value from the block is stringified and used to replace the delimited text (including delimiters).
If the block returns null then no change is performed at that point. In this case the returned string will contain the delimiters, the contents and any white space.
Returns the string with the substitutions performed.
Examples

"foo {{bar}} baz".substitute: "-0-"              // => "foo -0- baz"
"foo {{16}} baz".substitute: (int.parse it) + 1  // => "foo 17 baz"
"x {{ y }} z".substitute: null                   // => "x {{ y }} z"
"f [hest] b".substitute --open="[" --close="]": "horse"  // => "f horse b"
"x {{b}} z".substitute: { "a": "hund", "b": "kat" }[it]  // => "x kat z"

Returns a string where all ASCII upper case characters have been replaced with their lower case equivalents.
Non-ASCII characters are unchanged.

Returns a string where all ASCII lower case characters have been replaced with their upper case equivalents.
Non-ASCII characters are unchanged.

Writes the raw UTF-8 bytes of the string to a new ByteArray.

to-byte-array start/any end/any -> ByteArray
Deprecated. Use to-byte-array on a string slice instead.

to-string from/any=0 to/any=size -> string
Equivalent to copy, but can be used when you have either a ByteArray or a string.

Converts the string to little-endian UTF-16 and writes the raw UTF-16 bytes to a new ByteArray.

Removes leading and trailing whitespace.
Returns the trimmed string.
Advanced
Whitespace is defined by the Unicode White_Space property (version 6.2 or later). It furthermore includes the BOM character 0xFEFF.
As of Unicode 6.3 these are:

  0009..000D    ; White_Space # Cc   <control-0009>..<control-000D>
  0020          ; White_Space # Zs   SPACE
  0085          ; White_Space # Cc   <control-0085>
  00A0          ; White_Space # Zs   NO-BREAK SPACE
  1680          ; White_Space # Zs   OGHAM SPACE MARK
  2000..200A    ; White_Space # Zs   EN QUAD..HAIR SPACE
  2028          ; White_Space # Zl   LINE SEPARATOR
  2029          ; White_Space # Zp   PARAGRAPH SEPARATOR
  202F          ; White_Space # Zs   NARROW NO-BREAK SPACE
  205F          ; White_Space # Zs   MEDIUM MATHEMATICAL SPACE
  3000          ; White_Space # Zs   IDEOGRAPHIC SPACE

  FEFF          ; BOM                ZERO WIDTH NO_BREAK SPACE

trim --left/True -> string
Removes leading whitespace.
Variant of trim.

trim --right/True -> string
Removes trailing whitespace.
Variant of trim.

trim --left/True prefix/string -> string
Removes a leading prefix (if present).
Returns this instance verbatim, if it doesn't start with prefix.
Examples

"http://www.example.com".trim --left "http://" // => "www.example.com"
str := "foobar"
str.trim --left "foo"  // => "bar"
str.trim --left "bar"  // => "foobar"
str.trim --left "gee"  // => "foobar"

trim --left/True prefix/string [--if-absent] -> string
Removes a leading prefix.
Calls if-absent if this instance does not start with prefix. The argument to the block is this instance.
Examples

"https://www.example.com".trim --left "http://" --if-absent=: it.trim --left "https://"  // => "www.example.com"
str := "foobar"
str.trim --left "foo" --if-absent=: "not_used" // => "bar"
str.trim --left ""    --if-absent=: "not_used" // => "foobar"
str.trim --left "gee" --if-absent=: it         // => "foobar"   (the default behavior)
str.trim --left "gee" --if-absent=: throw "missing prefix" // ERROR

trim --right/True suffix/string -> string
Removes a trailing suffix (if present).
Returns this instance verbatim, if it doesn't end with suffix.
Examples

"hello.toit".trim --right ".toit"  // => "hello"
str := "foobar"
str.trim --right "bar"  // => "foo"
str.trim --right "foo"  // => "foobar"
str.trim --right "gee"  // => "foobar"

trim --right/True suffix/string [--if-absent] -> string
Removes a trailing suffix.
Calls if-absent if this instance does not end with suffix. The argument to the block is this instance.
Examples

str := "foobar"
str.trim --right "bar" --if-absent=: "not_used" // => "bar"
str.trim --right ""    --if-absent=: "not_used" // => "foobar"
str.trim --right "gee" --if-absent=: it         // => "foobar"   (the default behavior)
str.trim --right "gee" --if-absent=: throw "missing suffix" // ERROR

write-to-byte-array byte-array/ByteArray -> any
Writes the raw UTF-8 bytes of the string to an existing ByteArray.

write-to-byte-array byte-array/ByteArray dest-index/any -> any
Writes the raw UTF-8 bytes of the string to the given offset of an existing ByteArray.

write-to-byte-array byte-array/ByteArray start/any end/any dest-index/any -> any
Deprecated. Use write-to-byte-array on a string slice instead.

write-to-byte-array byte-array/ByteArray --at/int from/int to/int -> none