从 Lua5.3
开始, Lua
语言引入了一个用于操作 UTF-8
编码的 Unicode
字符串的标准库。当然,在引入这个标准库之前, Lua
语言也提供了对 UTF-8
字符串的合理支持。
UTF-8
是Web环境中用于Unicode的主要编码之一。由于 UTF-8
编码与ASCII编码部分兼容,所以 UTF-8
对于Lua语言来说也是一种理想的编码方式。这种兼容性保证了用于ASCII字符串的一些字符串操作技巧无需修改就可以用于 UTF-8
字符串。
UTF-8
使用变长的多个字节来编码一个 Unicode
字符。例如, UTF-8
编码使用一个字节 65
来代表 A
,使用两个字节 215-144
代表希伯来语(Hebrew)字符 Aleph
(其在 Unicode
中的编码是 1488
)。 UTF-8
使用一个字节表示所有 ASCII
范围内的字符(小于 128
)。对于其他字符,则使用字节序列表示,其中第一个字节的范围是 [194, 244]
,而后续的字节范围是 [128, 191]
。更准确地说,对于两个字节组成的序列来说,第一个字节的范围是 [194, 223]
;对于三个字节组成的序列来说,第一个字节的范围是: [224, 239]
;对于四个字节组成的序列来说,第一个字节的范围是 [24., 224]
,这些范围相互之间均不重叠。这种特点保证了任意字符对应的字节序列不会在其他字符对应的字节序列中出现。特别地,一个小于 128
的字节永远不会出现在多字节序列中,它只会代表与之对应的 ASCII
字符。
Lua
语言中的一些机制对 UTF-8
字符串来说同样“有效”。由于 Lua
语言使用 8
个字节来编码字符串,所以可以像操作其他字符串一样读写和存储 UTF-8
字符串。字符串常量也可以包含 UTF-8
数据(当然,读者可能需要使用支持 UTF-8
编码的编辑器来处理使用 UTF-8
编码的源文件)。字符串连接对 UTF-8
字符串同样适用。对字符串的比较(小于、小于等于等等)会按照 Unicode
编码中的字符代码顺序进行。
Lua
语言的操作系统库和输入输出是与对应系统之间的主要接口,所以他们是否支持 UTF-8
取决于对应的操作系统。例如,在 Linux
操作系统下文件名使用 UTF-8
编码,而在 Windows
操作系统下文件名使用 UTF-16
编码。因此,如果要在 Windows
操作系统中处理 Unicode
文件名,那么要么使用额外的库,要么就修改 Lua
语言的标准库。
函数 reverse
、 upper
、 lower
、 byte
和 char
不适用与 UTF-8
字符串,这是因为他们针对的都是一字节字符。函数 string.format
和 string.rep
适用与 UTF-8
字符串(格式选项 '%c'
除外,该格式选项针对一字节字符)。函数 string.len
和 string.sub
可以用于 UTF-8
字符串,其中的索引以字节为单位而不是以字符为单位。通常,这些函数就够用了。
函数 utf8.len
返回指定字符串中 UTF-8
字符(代码点)的个数。此外,该函数还会验证字符串,如果该函数发现字符串中包含无效的字节序列,则返回 false
外加第一个无效字节的位置。
函数 utf8.char
和 utf8.codepoint
在 UTF-8
环境下等价 于string.char
和 string.byte
提示
utf8
库中大多数函数使用字节为索引。例如,调用 string.codepoint(s, i, j)
时 i
和 j
都会被当做字符串 s
中的字节位置。如果想使用字符位置作为索引,那么可以通过函数 utf8.offset
把字符位置转换为字节位置。
utf8
标准库中的最后一个函数是 utf8.codes
,该函数用于遍历 UTF-8
字符串中的每一个字符。
不幸的是,除了上述的内容外, Lua
语言没有再提供其他机制。 Unicode
具有如此多稀奇古怪的特性,以至于想从特定的语言中抽象出其中的任意一个概念基本上是不太可能的。由于 Unicode
编码的字符和字素(grapheme)之间没有一对一个关系,所以甚至连字符的概念都是模糊的。其他诸如字母之类的基本概念在不同语系中也有差异。由于这些复杂性的存在,如果想支持完整的 Unicode
就需要巨大的表,而这又与 Lua
语言精简的大小想矛盾。因此,对于这些特殊需求来说,最好的选择就是使用外部库。