utf8Backported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau: utf8.codes, utf8.codepoint, utf8.len
Luau
utf8.codes, utf8.codepoint, utf8.lenUnicode support is limited
This library only supports codepoints (not graphemes) and its API is awkward for everyday text work. Most text deals in graphemes or grapheme clusters and should handle all languages correctly. We're looking to give Luwu full Unicode support, including graphemes, grapheme clusters and Unicode-aware string library functions.
The utf8 library encodes, decodes, counts and iterates the Unicode characters in a UTF-8 string.
A string holds bytes, and text in a string is UTF-8 by convention. A character outside ASCII takes two to four bytes. #s and the string library count bytes rather than characters.
Its decoding functions treat overlong encodings, surrogates (U+D800 to U+DFFF) and values above U+10FFFF as invalid UTF-8.
Summary
function utf8.char(...: number): stringutf8.charBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Encodes codepoints as a UTF-8 string
utf8.charpattern: stringutf8.charpatternBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Holds a pattern that matches one UTF-8 character
function utf8.codes(s: string): ((string, number) -> (number, number), string, number)utf8.codesBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau- Iterates over the byte indices and codepoints of a string
function utf8.codepoint(s: string, i: number?, j: number?): ...numberutf8.codepointBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau- Returns the codepoints of the characters in a range
function utf8.len(s: string, i: number?, j: number?): (number?, number?)utf8.lenBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau- Counts the characters in a string
function utf8.offset(s: string, n: number, i: number?): number?utf8.offsetBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau- Returns the byte index where a character starts
utf8.charBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
function utf8.char(...: number): stringReturns a string containing the UTF-8 encoding of every codepoint passed in, where a codepoint is the number Unicode assigns to a character: 72 for H, 233 for é and 0x1F600 for 😀. Because ASCII characters encode to a single byte and everything else takes two to four, the three characters below add up to seven bytes.
const face = utf8.char(72, 233, 0x1F600)
print(face)
print(#face)Hé😀 7
Every codepoint has to fall between 0 and 0x10FFFF, the highest one Unicode defines, and anything outside that range raises value out of range. Fractions are truncated rather than rejected, and calling utf8.char with no arguments returns an empty string.
Surrogates (0xD800 to 0xDFFF) are the one gap in that range: they aren't characters at all but the halves of the pairs UTF-16 uses to write codepoints above 0xFFFF. Even though utf8.len, utf8.codes and utf8.codepoint reject the result as invalid UTF-8, utf8.char encodes them anyway.
const surrogate = utf8.char(0xD800)
print(#surrogate)
print(utf8.len(surrogate))3 nil 1
utf8.charpatternBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
utf8.charpattern: stringA pattern for string.gmatch and the other pattern functions that matches one UTF-8 character at a time. It's the same string Lua 5.3 defined, "[\0-\x7F\xC2-\xF4][\x80-\xBF]*": one byte that can start a character (ASCII, or a lead byte from 0xC2 to 0xF4) followed by any number of continuation bytes from 0x80 to 0xBF.
for character in string.gmatch("ñ€", utf8.charpattern) do
print(`{character} takes {#character} bytes`)
endñ takes 2 bytes € takes 3 bytes
Because the * never checks how many continuation bytes the lead byte calls for, the pattern only works on text that's already valid UTF-8. On malformed input it glues extra continuation bytes onto the character before them and skips stray ones without complaint. When a string comes from somewhere you don't trust, check it with utf8.len first: on bad input it returns nil and the position of the first bad byte instead of a count.
const broken = "A\xC3\xA9\xA9B"
for character in string.gmatch(broken, utf8.charpattern) do
print(#character)
end
print(utf8.len(broken))1 3 1 nil 4
utf8.codesBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau
function utf8.codes(s: string): ((string, number) -> (number, number), string, number)Returns an iterator for a for loop that gives the byte index and the codepoint of each character in s. Invalid UTF-8 raises an error.
for index, codepoint in utf8.codes("hé!") do
print(`{index}: {codepoint}`)
end1: 104 2: 233 4: 33
utf8.codepointBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau
function utf8.codepoint(s: string, i: number?, j: number?): ...numberReturns the codepoint of each character that starts between byte indices i and j. i defaults to 1 and j defaults to i. With one index or none, codepoint returns the codepoint of the character that starts there.
Negative indices count back from the end. An index outside the string or invalid UTF-8 raises an error.
print(utf8.codepoint("é"))
const first, second = utf8.codepoint("hé", 1, -1)
print(first)
print(second)233 104 233
utf8.lenBackported from Lua 5.3 by Luau 0.501 or earlier.
Changed by Luau 0.603: Rejects UTF-16 surrogate code points.
Luau
function utf8.len(s: string, i: number?, j: number?): (number?, number?)Returns the number of characters that start between byte indices i and j. i defaults to 1 and j defaults to -1, the last byte. Negative indices count back from the end.
When len finds invalid UTF-8, it returns nil followed by the index of the first invalid byte.
print(#"héllo")
print(utf8.len("héllo"))
const count, bad_index = utf8.len("h\xFFi")
print(count)
print(bad_index)6 5 nil 2
utf8.offsetBackported from Lua 5.3 by Luau 0.501 or earlier.
Luau
function utf8.offset(s: string, n: number, i: number?): number?Returns the byte index where the nth character counting from byte index i starts. A positive n counts forward and 1 is the character at i. A negative n counts backward from i. When n is 0, offset returns the start of the character that byte i belongs to.
i defaults to 1 when n is positive and to #s + 1 otherwise. utf8.offset(s, -1) gives the start of the last character. When there's no such character, offset returns nil. A nonzero n with an i in the middle of a character raises an error.
const word = "héllo"
print(utf8.offset(word, 3))
print(utf8.offset(word, -1))
print(utf8.offset(word, 0, 3))
print(utf8.offset(word, 9))4 6 2 nil