stringInherited from Lua 5.1's standard library.
LuaAdded by Luau: string.split, string.pack, string.packsize, string.unpack
Changed by Luau: string.format
LuauAdded by Luwu: string.contains, string.startswith, string.endswith, string.replace, string.trim, string.trimstart, string.trimend
Changed by Luwu: string.split
Luwu
string.split, string.pack, string.packsize, string.unpackstring.formatstring.contains, string.startswith, string.endswith, string.replace, string.trim, string.trimstart, string.trimendstring.splitA string is an immutable sequence of bytes that usually represents text. The string library slices, searches, splits, formats and packs strings. Method calls on strings call functions in the string library: s:upper() functions the same as string.upper(s).
Please note that unlike many high-level programming languages, Luwu strings are not UTF-8 encoded. Lua and Luau code from before the buffer primitive was added often treats strings as the primary vehicle for representing arbitrary bytes.
The utf8 library handles Unicode codepoints, but it's not ergonomic to use and it doesn't yet support the full featureset you'd expect for Unicode. We're looking forward to adding full Unicode support in Luwu, including graphemes, grapheme clusters, Unicode-aware string library functions, and more.
In modern Luau, you may see users and libraries avoid string method calls like the plague. This is because in the Luau VM, string method calls are 2x as expensive as string library calls. We've fixed this: with FFlag LuwuStringExtensions enabled, string method calls are just as fast as string library calls.
find, match, gmatch and gsub search with Patterns, strings that describe the text to look for. split and the Luwu additions like contains and replace compare plain text and never read patterns.
const line = "nyla, ada, lin"
const names = line:split(", ")
print(names[2]:upper())
print(line:contains("ada"))
print(line:match("%a+$"))ADA true lin
Summary
Bytes and slicing
function string.byte(s: string, i: number?, j: number?): ...numberstring.byteInherited from Lua 5.1's standard library.
Lua- Returns the numeric codes of a string's bytes
function string.char(...: number): stringstring.charInherited from Lua 5.1's standard library.
Lua- Builds a string from byte codes
function string.len(s: string): numberstring.lenInherited from Lua 5.1's standard library.
Lua- Returns the number of bytes in a string
function string.sub(s: string, i: number, j: number?): stringstring.subInherited from Lua 5.1's standard library.
Lua- Returns the part of a string between two indices
function string.rep(s: string, n: number): stringstring.repInherited from Lua 5.1's standard library.
Lua- Repeats a string
function string.reverse(s: string): stringstring.reverseInherited from Lua 5.1's standard library.
Lua- Reverses a string's bytes
function string.lower(s: string): stringstring.lowerInherited from Lua 5.1's standard library.
Lua- Changes ASCII letters to lowercase
function string.upper(s: string): stringstring.upperInherited from Lua 5.1's standard library.
Lua- Changes ASCII letters to uppercase
Searching with patterns
function string.find( s: string, pattern: string, init: number?, plain: boolean? ): (number?, number?, ...string)string.findInherited from Lua 5.1's standard library.
Lua- Finds a pattern or plain text and returns where it is
function string.match(s: string, pattern: string, init: number?): ...string?string.matchInherited from Lua 5.1's standard library.
Lua- Returns the captures of a pattern's first match
function string.gmatch(s: string, pattern: string): () -> ...stringstring.gmatchInherited from Lua 5.1's standard library.
Lua- Loops over every match of a pattern
function string.gsub( s: string, pattern: string, replacement: string | { [string]: string } | (...string) -> string?, max: number? ): (string, number)string.gsubInherited from Lua 5.1's standard library.
Lua- Replaces every match of a pattern
Searching plain text
function string.contains(s: string, needle: string, ...: string): booleanstring.containsAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Checks whether a string contains any of the given strings
function string.startswith(s: string, prefix: string, ...: string): booleanstring.startswithAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Checks whether a string starts with any of the given strings
function string.endswith(s: string, suffix: string, ...: string): booleanstring.endswithAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Checks whether a string ends with any of the given strings
function string.replace(s: string, old: string, new: string, max: number?): stringstring.replaceAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Replaces plain text
Splitting and trimming
function string.split(s: string, separator: string?, maxpieces: number?): { string }string.splitAdded by Luau 0.501 or earlier.
LuauChanged by Luwu: Takes a maximum number of pieces.
Luwu- Splits a string at a separator
function string.trim(s: string, ...: string): stringstring.trimAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Removes whitespace or the given strings from both ends
function string.trimstart(s: string, ...: string): stringstring.trimstartAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Removes whitespace or the given strings from the start
function string.trimend(s: string, ...: string): stringstring.trimendAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the
flaggedLuwuStringExtensionsFFlag.- Removes whitespace or the given strings from the end
Formatting
function string.format(format: string, ...: unknown): stringstring.formatInherited from Lua 5.1's standard library.
LuaChanged by Luau 0.539: The %* specifier formats any value the way tostring does.
Luau- Formats values into a string with printf-style specifiers
Binary data
function string.pack(format: string, ...: unknown): stringstring.packBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Encodes values as binary data
function string.packsize(format: string): numberstring.packsizeBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Returns how many bytes a pack format takes
function string.unpack(format: string, data: string, init: number?): ...unknownstring.unpackBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Decodes binary data
Bytes and slicing
string.byteInherited from Lua 5.1's standard library.
Lua
function string.byte(s: string, i: number?, j: number?): ...numberReturns the numeric code of each byte of s from index i to index j. i defaults to 1 and j defaults to i. With one index or none, byte returns a single code.
Negative indices count back from the end. Indices past either end are clamped to the string. When the range is empty, byte returns no values.
print(string.byte("A"))
const first, last = string.byte("Hi", 1, -1)
print(first)
print(last)65 72 105
string.charInherited from Lua 5.1's standard library.
Lua
function string.char(...: number): stringReturns a string with one byte for each argument. Each value must be an integer from 0 to 255.
print(string.char(72, 105, 33))Hi!
string.lenInherited from Lua 5.1's standard library.
Lua
function string.len(s: string): numberReturns the number of bytes in s. It's the same as #s. A character outside ASCII takes more than one byte. utf8.len counts characters.
print(string.len("héllo"))
print(utf8.len("héllo"))6 5
string.subInherited from Lua 5.1's standard library.
Lua
function string.sub(s: string, i: number, j: number?): stringReturns the part of s from index i to index j, both included. j defaults to -1, the last byte.
Negative indices count back from the end and indices past either end are clamped to the string. When i comes after j, sub returns an empty string.
const greeting = "hello world"
print(greeting:sub(1, 5))
print(greeting:sub(-5))hello world
string.repInherited from Lua 5.1's standard library.
Lua
function string.rep(s: string, n: number): stringReturns s repeated n times. A zero or negative n gives an empty string.
print(string.rep("ab", 3))ababab
string.reverseInherited from Lua 5.1's standard library.
Lua
function string.reverse(s: string): stringReturns s with its bytes in reverse order. A character that takes more than one byte comes out as invalid UTF-8.
print(("stressed"):reverse())desserts
string.lowerInherited from Lua 5.1's standard library.
Lua
function string.lower(s: string): stringReturns a copy of s with every ASCII uppercase letter changed to lowercase. Other bytes stay as they are, letters outside ASCII included.
print(("ÉCOLE"):lower())École
string.upperInherited from Lua 5.1's standard library.
Lua
function string.upper(s: string): stringReturns a copy of s with every ASCII lowercase letter changed to uppercase. Other bytes stay as they are, letters outside ASCII included.
print(("école"):upper())éCOLE
Searching with patterns
string.findInherited from Lua 5.1's standard library.
Lua
function string.find(
s: string,
pattern: string,
init: number?,
plain: boolean?
): (number?, number?, ...string)Looks for the first match of pattern in s that starts at index init or later. It returns the indices where the match starts and ends, followed by the pattern's captures. When nothing matches, find returns nil.
init defaults to 1 and a negative init counts back from the end. When plain is true, find compares pattern as plain text. Searching for user input or text with . or % in it needs plain, or string.contains when the indices don't matter.
const start, finish = string.find("key = value", " = ")
print(start)
print(finish)
print((string.find("a.b", ".")))
print((string.find("a.b", ".", 1, true)))4 6 1 2
string.matchInherited from Lua 5.1's standard library.
Lua
function string.match(s: string, pattern: string, init: number?): ...string?Looks for the first match of pattern in s that starts at index init or later. It returns the pattern's captures, or the whole match when the pattern has no captures. When nothing matches, match returns nil. init defaults to 1 and a negative init counts back from the end.
print(string.match("level 12", "%d+"))
const user, host = string.match("ada@example.com", "^(.-)@(.+)$")
print(user)
print(host)
print(string.match("no digits", "%d+"))12 ada example.com nil
string.gmatchInherited from Lua 5.1's standard library.
Lua
function string.gmatch(s: string, pattern: string): () -> ...stringReturns an iterator that finds each match of pattern in s in turn. Each call returns the pattern's captures, or the whole match when the pattern has no captures. A for loop calls it until it runs out of matches.
A ^ at the start of the pattern doesn't anchor it here. It matches a literal ^ instead.
for word in string.gmatch("one two three", "%a+") do
print(word)
end
for key, value in string.gmatch("hp=30, gold=12", "(%w+)=(%w+)") do
print(`{key} is {value}`)
endone two three hp is 30 gold is 12
string.gsubInherited from Lua 5.1's standard library.
Lua
function string.gsub(
s: string,
pattern: string,
replacement: string | { [string]: string } | (...string) -> string?,
max: number?
): (string, number)Replaces every match of pattern in s and returns the new string followed by the number of matches it replaced. max limits how many matches are replaced. What each match becomes depends on replacement:
- A string is the replacement text. In it,
%1to%9stand for the captures,%0for the whole match and%%for a percent sign. Any other%is an error. - A table is indexed with the first capture, or the whole match when there are no captures.
- A function is called with the captures, or the whole match when there are no captures.
When the table or function gives nil or false, the match stays as it was. Passing gsub's result straight into another call passes the count too. Wrap the call in parentheses to keep only the string, or use string.replace to replace plain text.
print((string.gsub("hello world", "o", "0")))
print((string.gsub("Ada Lovelace", "(%a+) (%a+)", "%2, %1")))
const prices: { [string]: string } = { apple = "1.20", kiwi = "0.80" }
print((string.gsub("apple and kiwi", "%a+", prices)))hell0 w0rld Lovelace, Ada 1.20 and 0.80
Searching plain text
string.containsAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.contains(s: string, needle: string, ...: string): booleanReturns whether s contains needle or any of the other strings given. The strings are compared byte by byte and are never patterns. An empty needle is in every string.
print(("a.b"):contains("."))
print(("abc"):contains("."))
print(("error: disk full"):contains("warning", "error"))true false true
string.startswithAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.startswith(s: string, prefix: string, ...: string): booleanReturns whether s starts with prefix or with any of the other strings given. The strings are compared byte by byte and are never patterns. An empty prefix matches every string.
print(("# a comment"):startswith("#", "--"))
print(("-- a comment"):startswith("#", "--"))
print(("value"):startswith("#", "--"))true true false
string.endswithAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.endswith(s: string, suffix: string, ...: string): booleanReturns whether s ends with suffix or with any of the other strings given. The strings are compared byte by byte and are never patterns. An empty suffix matches every string.
print(("main.luwu"):endswith(".luau", ".luwu"))
print(("main.lua"):endswith(".luau", ".luwu"))true false
string.replaceAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.replace(s: string, old: string, new: string, max: number?): stringReplaces occurrences of old in s with new from left to right without overlapping and returns the new string. Both old and new are plain text: . and % need no escaping.
max limits the number of replacements and a max of 0 returns s unchanged. A negative max or an empty old is an error. Unlike string.gsub, replace returns only the string.
print(("a.b.c"):replace(".", "/"))
print(("100%"):replace("%", " percent"))
print(("aaaa"):replace("a", "b", 2))
print(("aaa"):replace("aa", "b"))a/b/c 100 percent bbaa ba
Splitting and trimming
string.splitAdded by Luau 0.501 or earlier.
LuauChanged by Luwu: Takes a maximum number of pieces.
Luwu
function string.split(s: string, separator: string?, maxpieces: number?): { string }Splits s at each occurrence of separator and returns the pieces as an array. separator defaults to "," and is compared as plain text, not as a pattern. Two separators in a row leave an empty piece between them. An empty separator splits s into one-byte strings.
With maxpieces, split returns at most that many pieces and the last piece holds the rest of the string. maxpieces must be at least 1.
const parts = string.split("a,b,,c")
print(#parts)
print(table.concat(parts, "|"))
const key, value = table.unpack(string.split("role = admin = yes", " = ", 2))
print(key)
print(value)4 a|b||c role admin = yes
string.trimAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.trim(s: string, ...: string): stringRemoves whitespace, or the strings given, from both ends of s.
With no strings, trim removes every character with Unicode's White_Space property and the byte order mark (U+FEFF). That covers the ASCII tab, line feed, vertical tab, form feed, carriage return and space. It also covers characters the %s pattern class misses, like the no-break space (U+00A0) and the ideographic space (U+3000).
With strings, trim removes only those and leaves whitespace alone. Each string is a whole token rather than a set of characters. trim keeps removing tokens from an end until none is left there, and at each step the first given string that matches is removed. Empty strings are ignored.
print(`[{(" hello "):trim()}]`)
print(`[{("\u{FEFF}name,age\u{3000}\r\n"):trim()}]`)
print(("--title--"):trim("-"))
print(`[{(" --title-- "):trim("-")}]`)[hello] [name,age] title [ --title-- ]
string.trimstartAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.trimstart(s: string, ...: string): stringWorks like string.trim at the start of s only.
print(`[{(" indented "):trimstart()}]`)
print(("////path"):trimstart("//"))[indented ] path
string.trimendAdded by Luwu.
LuwuAvailable in luwu/integration and gated by an FFlag. We anticipate this feature to be released and enabled by default in 0.1.0.
Needs the LuwuStringExtensions FFlag.
flagged
LuwuStringExtensions FFlag.function string.trimend(s: string, ...: string): stringWorks like string.trim at the end of s only. A token like "\r\n" is removed only as a pair. Pass "\r" and "\n" separately to remove either one.
const function show(s: string): string
return s
:replace("\r", "\\r")
:replace("\n", "\\n")
end
print(show(("/usr/local/"):trimend("/")))
print(show(("line\r\n\r\n"):trimend("\r\n")))
print(show(("line\n"):trimend("\r\n")))
print(show(("line\r\n\n"):trimend("\r", "\n")))/usr/local line line\n line
Formatting
string.formatInherited from Lua 5.1's standard library.
LuaChanged by Luau 0.539: The %* specifier formats any value the way tostring does.
Luau
function string.format(format: string, ...: unknown): stringReturns format with each specifier replaced by the next argument. Specifiers start with % and follow C's printf:
| Specifier | Writes | Example | Result |
|---|---|---|---|
%d | an integer in decimal | string.format("%d", 42) | "42" |
%i | an integer in decimal, the same as %d | string.format("%i", 42) | "42" |
%d | a number with a fraction truncated toward zero | string.format("%d", -7.9) | "-7" |
%u | an unsigned integer in decimal | string.format("%u", 42) | "42" |
%o | an integer in octal | string.format("%o", 8) | "10" |
%x | an integer in lowercase hexadecimal | string.format("%x", 255) | "ff" |
%X | an integer in uppercase hexadecimal | string.format("%X", 255) | "FF" |
%c | the byte with the given code | string.format("%c", 65) | "A" |
%e | a number in scientific notation | string.format("%e", 1234.5) | "1.234500e+03" |
%E | a number in scientific notation with a capital E | string.format("%E", 1234.5) | "1.234500E+03" |
%f | a number in decimal notation | string.format("%f", 3.14159) | "3.141590" |
%g | a number the shorter way of %e and %f | string.format("%g", 0.0001) | "0.0001" |
%G | a number the shorter way of %E and %f | string.format("%G", 1e-10) | "1E-10" |
%q | a string in double quotes, escaped so Luwu reads it back as the same string | string.format("%q", "say \"hi\"") | "\"say \\\"hi\\\"\"" |
%s | a string or number as it is | string.format("%s", "gold") | "gold" |
%* | any value, converted the way tostring converts it | string.format("%*", true) | "true" |
%% | a percent sign | string.format("%%") | "%" |
Between the % and the letter, a specifier can have flags (-, +, space, # and 0), a width and a precision, like %-8s or %.2f. A width or precision has at most two digits and * can't stand in for one. %* takes no flags, width or precision.
print(string.format("%-6s|%5.1f|%03d", "gold", 12.345, 7))
print(string.format("%q", 'say "hi"'))
print(string.format("%* and %*", true, nil))gold | 12.3|007 "say \"hi\"" true and nil
Binary data
string.packBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
function string.pack(format: string, ...: unknown): stringEncodes the arguments as binary data according to format and returns the bytes as a string. Each option in format encodes one value or sets how the next ones are encoded:
| Option | Encodes | Size |
|---|---|---|
< | switches to little-endian for the options after it | none |
> | switches to big-endian | none |
= | switches to the machine's native byte order | none |
!n | aligns values to at most n bytes, or 8 when n is left out | none |
b | a signed integer | 1 bytes |
B | an unsigned integer | 1 bytes |
h | a signed integer | 2 bytes |
H | an unsigned integer | 2 bytes |
j | a signed integer | 4 bytes |
J | an unsigned integer | 4 bytes |
l | a signed integer | 8 bytes |
L | an unsigned integer | 8 bytes |
T | an unsigned integer | 4 bytes |
in | a signed integer of n bytes, from 1 to 16 | n bytes, or 4 when n is left out |
In | an unsigned integer of n bytes, from 1 to 16 | n bytes, or 4 when n is left out |
f | a float | 4 bytes |
d | a float | 8 bytes |
n | a float | 8 bytes |
sn | a string after its length, which takes n bytes, or 4 when n is left out | n bytes plus the string |
z | a string followed by a zero byte. The string can't contain zeros. | the string plus 1 byte |
cn | a string of exactly n bytes, padded with zeros | n bytes |
x | one byte of padding | 1 bytes |
Xop | padding up to the alignment of option op | up to op's size |
Spaces between options are ignored. These sizes are the same on every platform. Lua 5.x sizes h, j, l, T and i after the C compiler's types instead.
const data = string.pack("<I2 z", 513, "hi")
print(#data)
const number, name, next_index = string.unpack("<I2 z", data)
print(number)
print(name)
print(next_index)5 513 hi 6
string.packsizeBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
function string.packsize(format: string): numberReturns the number of bytes string.pack produces for format. The variable-length options s and z are an error here.
print(string.packsize("i4 h d"))14
string.unpackBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
function string.unpack(format: string, data: string, init: number?): ...unknownDecodes data according to format and returns the decoded values followed by the index of the first byte it didn't read. Decoding starts at byte init, which defaults to 1. string.pack lists the format options.