http://lua-users.org/wiki/LuaUnicode
这里尝试着回答一下LuaFaq问题:
我可以使用Unicode字符串么?或者,Lua支持Unicode么?
简言之,可以,不支持。Lua只有极为精简的支持和足够的辅助功能,就没有其它的了。Unicode是一个庞大而复杂的标准,像是“Lua是否支持Unicode”是比较模糊地问题。
一些问题如下:
我可以存取Unicode字符串么?
我的Lua程序能用Unicode写么?
我可以比较Unicode字符串是否相同么?
字符串排序。
模式匹配。
我可以取得一个Unicode字符串的长度么?
支持括号型匹配,双向打印,随意的字符串组合以及各种高品质排版技术中产生的问题。
Lua字符串使用8-bit,所以简单的应用是支持的(比如存取操作)。但对于其他复杂操作没有内建支持。想知道更多故事,往下看。
Unicode字符串和Lua字符串
一个Lua字符串是一个至少8bit值的任意组合;可以直接映射到C编译器中的char类型(可能会比8bits更宽,但是一定会保证有8bits)。Lua没有保留任意字符,包括NUL。这意味着你可以成功地在Lua中存储UTF-8字符串。
注意UTF-8只是存储Unicode字符串的一种可能。还有其他编码模式,包括UTF-16和UTF-32以及它们的大端、小端变体。无论如何,所有这些都仅仅是8位字节的组合,可以毫无问题的存储到Lua字符串中。
在Lua中字符串的输入输出(使用IO库)使用C的stdio库,ANSI C不需要stdio库来处理任意8位字节顺序,除非是二进制模式。更进一步说,在非二进制模式,一些8位字节顺序可以转换到其他字符(为了处理不同平台上的换行符)。
这可能影响到处理非二进制的而且不是UTF-8格式的Unicode字符串文件输入输出。UTF-8字符串可能还是安全的,因为UTF-8不使用控制字符如\r \n作为多字节解析的一部分。而且这也不是一定的。如果你需要个确定答案,你应该使用二进制模式输入输出(binary-mode)。(如果你使用二进制模式,行结尾符将不会被转换)。
Unix文件输入输出很长时间里都是8字节整的。如果你不关心可移植性,仅仅在Unix或者类Unix系统上工作,可以完全不用担心前面提到的。
如果你使用Unicode是限制在传送字符串到外部支持Unicode的程序员,应该是不用担心的。例如,你应该可以从数据库取得一个Unicode字符串传递到一个识别Unicode的图形库中。但是也看看下面关于模式匹配和字符串等同比较的章节。
Unicode Lua 程序
字面型Unicode字符串可以出现在你的Lua程序组,UTF-8编码的字符串可以直接用8位字符形式或者使用\ddd语法(注意ddd是一个十进制数)。无论如何没有编码多字节顺序的功能(比如\U+20B4);你可能不得不手动把它们编码成UTF-8格式或者以正确的大端或小端顺序塞进单独的8位字节数值中(针对UTF-16或者UTF-32)。
如果你不是使用某个char比8位更长字节的操作系统,就不能用任意的Unicode字符作为Lua标示符(变量名或者类似)。你可能使用ANSI以外的8位字符,Lua使用C函数isalpha和isalnum来辨别正确的可作为标示符的字符,所以这与当前locale配置有关。实话讲,使用ANSI以外的的字符做标示符不是一个好主意,因为你的程序可能没法在标准C locale下编译。
比较和排序
Lua字符串比较(使用==操作符)是通过逐个字节比较完成。这意味着==仅仅可以用来比较特殊的等同性,就是Unicode字符已经被正常化为正常可能之一(http://www.unicode.org/faq/normalization.html)。标准Lua库不提供任何正常化Unicode字符串的能力。相应的,非正常化的Unicode字符串也不能被可靠的用作table的key。
如果你想用Unicode类型的字符串等同比较,或者使用Unicode作为table的key,你不能保证你的字符串一定被正常化,然后你不得不自己写或者寻找一个正常化函数,这是一个很重要的练习!
Lua字符串比较操作符(<和<=)使用C语言函数strcoll,这个函数依赖于locale配置。这意味着两个字符串随着locale不同会得到不同的结果。例如,字符串使用西班牙语传统排序与威尔士语排序结果是不一样的。
也许你的操作系统实现了你想要的排序算法,这时候你可以使用它们,否则你可能要自己写函数来对Unicode字符串排序。这是一个更重要的练习。
UTF-8被设计成一个8位字节序的简单逐位比较可以得到同样的结果。UTF-32也是正确的,但是我不知道什么系统使用这个编码方式。不幸的是,简单的逐位比较没有被用作任何语言的排列顺序。
模式匹配
Lua的模式匹配的工作方式也是逐个字节的。通常情况下,这对于Unicode的模式匹配没有用处。即使有时候程序以你预想的方式工作了。例如,"%u"将不能匹配所有Unicode大写字符。你能匹配正常化后的Unicode字符串中的单个Unicode字符,但是你可能也担心字符串顺序。如果没有后续合并字符串,"a"将匹配一个UTF-8字符中的字符a。在UTF-16LE中你能匹配"a%z"。(记住你不能使用\0在Lua模式中)
长度和字符串索引
如果你想知道一个Unicode字符的长度,根据情况不同你会得到不同答案。
如果你想知道一个字符串占据多少个字节,比如你想拷贝到某个buffer中,现有的string.len函数可以工作。
你可能想知道Unicode字符串有多少字符。根据被使用的编码,一个单一的Unicode字符占用4个字节。只有UTF-32LE和UTF-32BE是常数长度编码(每个字符四字节);UTF-32是一个常数长度编码但是第一个元素应该是”字节序标记”。这不应该被看做字符(UTF-32和变体是最新版本Unicode4.0的一部分)。
一些UTF-16的实现确保所有字符都是两字节长,但是从Unicode3.0版本以后就不是这样。
很高兴的是UTF-8被设计成很容易计算Unicode字符串中符号个数;仅仅计算0x00到0x7f或者0xC2到0xF4范围内的八位字节数字个数。这些是UTF-8字符码的开始。0xC0, 0xC1, 0xF5到0xFF不能用于构成UTF-8字节序;八位字节在0x80到0xBF可以仅仅出现在第二个或者多位编码后面部分。记住你不能使用\0在Lua模式中。
例如,你想使用接下来的代码计算UTF-8字符个数,(对于计算某些非法字符,这是不正确的)
local _, count = string.gsub(unicode_string, "[^\128-\193]", "")
如果你想知道一个Unicode字符串占据多少打印列(当你使用固定宽度字体),又有不同的回答了。这是因为某些Unicode字符没有一个打印宽度,而另外一些有两倍的打印宽度。合并字符被用于给其他字符加重音,通常打印时不占据额外空间。
所以不同情况下至少有三种不同的长度可能。Lua提供了一种string.len,而其它你需要自己写函数。
有一个类似的问题就是通过位置索引字符串某个字符。string.sub(s, -3)会返回最后三个字节,但未必会返回最后三个字符。
你可能使用接下来代码段枚举UTF-8字节序:
for uchar in string.gfind(ustring, "([%z\1-\127\194-\244][\128-\191]*)")
do -- something
end
More sophisticated issues
As you might have guessed by now, Lua provides no support for things like bidirectional printing or the proper formatting of Thai accents. Normally such things will be taken care of by a graphics or typography library. It would of course be possible to interface to such a library that did these things if you had access to one.
There is a little string-like package [slnunicode] with upper/lower, len/sub and pattern matching for UTF-8.
See ValidateUnicodeString for a smaller library.
[ICU4Lua] is a Lua binding to ICU (International Components for Unicode [1]), an open-source library originally developed by IBM.
See UnicodeIdentifers for platform independent Unicode Lua programs.
《“Lua Unicode (Wiki翻译)”》 有 1 条评论
就目前而言,utf8最让lua国际化最好的办法。