Strings
Strings are useful for holding data that can be represented in text form.
Creating Strings
Strings are created using either single or double quotes, and can hold unicode text.
string1 = "A string value"
string2 = 'This is also a string value'
string3 = "こんにちは、世界"
Accessing Characters
The subscript operator reads a single character by index, counting from zero. An index outside the string reads as null.
console.log("Ghost"[0]) // >> G
console.log("Ghost"[9]) // >> null
Template Literals
A string written in backticks is a template literal, and interpolates any expression written inside ${}.
name = "Ghost"
count = 3
console.log(`Hello, ${name}!`)
// >> Hello, Ghost!
console.log(`there are ${count} item${count == 1 ? "" : "s"}`)
// >> there are 3 items
Each interpolated value is converted with its own string form — the same representation console.log() and format() already use — so no value needs an explicit toString() to appear cleanly.
console.log(`the list is ${[1, 2, 3]}`)
// >> the list is [1, 2, 3]
Interpolations nest, so a template literal can contain another one:
console.log(`outer ${`inner ${1 + 1}`}`)
// >> outer inner 2
+ means. "count: " + count is still a type error, because both sides of + have to be the same type. The template literal is the fluent way to build a string out of mixed types.Comparing Strings
Strings can be compared against each other using the == operator. This will compare strings in a case-sensitive manner.
string1 = "a"
string2 = "b"
// false
result = string1 == string2
Long Strings
Sometimes, your code will include strings which are very long. Rather than having lines that go on endlessly, or wrap at the whim of your editor, you may wish to specifically break the string into multiple lines in the source code without affecting the actual string contents.
You can achieve this using the + operator to append multiple strings together, like this:
longString = "This is a very long string which needs " +
"to wrap across multiple lines because " +
"otherwise the code will be unreadable."
A string may also simply span several lines literally, newlines included:
longString = "This string
spans two lines."
Methods
Every method that counts or addresses a position works in characters, not bytes, so multi-byte text behaves the way it looks.
charAt()
The charAt() method returns the one-character string at a position. A position outside the string answers "" rather than erroring.
console.log("Ghost".charAt(0)) // >> G
console.log("Ghost".charAt(99)) // >> (empty string)
console.log("こんにちは".charAt(1)) // >> ん
contains()
The contains() method reports whether a substring appears anywhere in the string. This is a plain substring search, not a pattern — use matches() for a regular expression.
result = "Ghost".contains("host")
// expected value: true
indexOf()
The indexOf() method returns the position of the first occurrence of a substring, or -1 if it never occurs.
console.log("banana".indexOf("an")) // >> 1
console.log("banana".indexOf("zz")) // >> -1
isEmpty()
The isEmpty() method reports whether the string has no characters — the same as length() == 0.
console.log("".isEmpty()) // >> true
console.log("Ghost".isEmpty()) // >> false
lastIndexOf()
The lastIndexOf() method returns the position of the last occurrence of a substring, or -1 if it never occurs.
console.log("banana".lastIndexOf("an")) // >> 3
padEnd()
The padEnd() method grows the string to a given length by repeating a pad string on the right. The pad defaults to a space, and is truncated to fit exactly.
console.log("7".padEnd(3)) // >> "7 "
console.log("ab".padEnd(5, ".")) // >> ab...
A string already at or past the requested length comes back unchanged.
padStart()
The padStart() method is padEnd() from the other side — the usual way to line numbers up.
console.log("7".padStart(3, "0")) // >> 007
console.log("abc".padStart(2)) // >> abc
repeat()
The repeat() method returns the string joined to itself a given number of times. A negative count is a value error.
console.log("ab".repeat(3)) // >> ababab
reverse()
The reverse() method returns a new string with its characters in the opposite order.
console.log("Ghost".reverse()) // >> tsohG
console.log("こんにちは".reverse()) // >> はちにんこ
slice()
The slice() method returns the characters from start up to, but not including, end. Leaving end off runs to the end of the string.
console.log("Ghost".slice(1)) // >> host
console.log("Ghost".slice(1, 3)) // >> ho
charAt(), which is lenient, slice() validates both ends and raises an index error when either falls outside the string. That is the rule across Ghost: reading a single position answers null or "" when it is out of range, while reading a range checks its bounds.find()
The find() method searches the string for a regular expression and returns the text that matched. The receiver is the string being searched, and the argument is the pattern — the same way round as JavaScript, PHP, and Python. When nothing matches, it returns an empty string.
found = "I need coffee".find("need (.*)")
// expected value: "need coffee"
Patterns use RE2 syntax. An invalid pattern is a value error naming the problem.
find(), findAll(), and matches() all read subject.method(pattern). Earlier versions of Ghost had the receiver and argument the other way around; code written against those needs the two swapped.findAll()
The findAll() method returns a list of every match in the string, in order. A pattern that matches nothing gives an empty list.
found = "a1 b2 c3".findAll("[a-z][0-9]")
// expected value: ["a1", "b2", "c3"]
Each entry is a whole match. findAll() does not report capture groups.
format()
The format() method formats according to a format specifier and returns the resulting string.
name = "Soma"
age = 3
message = "%s is %s years old.".format(name, age)
// expected value: "Soma is 3 years old."
Every argument is converted to its string form before being substituted, so %s is the formatter to use for any value — numbers included.
| Formatter | Description |
|---|---|
%s | any value |
%% | a literal percent sign |
endsWith()
The endsWith() method determines if the given string ends with the given value.
result = "This is my name".endsWith("name")
// expected value: true
length()
The length() method returns the number of characters in the given string. It counts unicode characters, not bytes.
length = "Ghost".length()
// expected value: 5
length = "こんにちは".length()
// expected value: 5
matches()
The matches() method reports whether a regular expression matches anywhere in the string. As with find(), the receiver is the string and the argument is the pattern.
result = "Ghost".matches("^Gh")
// expected value: true
replace()
The replace() method replaces a given string within the string.
replaced = "Ghost 0.x".replace("0.x", "1.x")
// expected value: "Ghost 1.x"
split()
The split() method splits a string into a list by the given delimiter.
segments = "one, two, three".split(", ")
// expected value: ["one", "two", "three"]
startsWith()
The startsWith() method determines if the given string begins wih the given value.
result = "This is my name".startsWith("This")
// expected value: true
toLowerCase()
The toLowerCase() method converts the given string to lowercase.
value = "GHOST".toLowerCase()
// expected value: "ghost"
toUpperCase()
The toUpperCase() method converts the given string to uppercase.
value = "ghost".toUpperCase()
// expected value: "GHOST"
toString()
The toString() method converts the given string to a string. May seem redundant in this instance but this method can be reliably called regardless of the type of value.
value = "Ghost".toString()
// expected value: "Ghost"
toNumber()
The toNumber() method converts the given string to a number. A string that isn't a number converts to 0 rather than raising an error.
value = "3.14".toNumber()
// expected value: 3.14
value = "ghost".toNumber()
// expected value: 0
trim()
The trim() method trims the given string.
value = " Ghost ".trim()
// expected value: "Ghost"
trimEnd()
The trimEnd() method trims the end of the given string.
value = " Ghost ".trimEnd()
// expected value: " Ghost"
trimStart()
The trimStart() method trims the start of the given string.
value = " Ghost ".trimStart()
// expected value: "Ghost "
includes (use contains), no at (use charAt), and no substring (use slice) — the spellings chosen are the ones lists already use.