lua5.3已经支持了utf8编码,也就是说,lua5.3的字符串变量所对应的字节数组是以UTF8编码方式组织的。我写了一个简单的例子,脚本中写入一段中文字符串,然后分别使用lua5.1和lua5.3的解释器打印,看看结果是什么样子的。lua5.1不支持utf8,是按照C的方式来处理字符串的,那么接收中文再打印出来时应该会有一些问题。如下:
1 print("中华人民共和国")
但是二者在控制台都完整地输出了原字符串。这个与推理就违背了,那么是什么原因呢?假设程序所在的文本文件是以A方式编码的(如utf8),解释器L读取文本文件拿到字节流,L知道字节流是以A方式编码的,而L中的字符串是以B方式编码的,那么L就要将字节流按A方式解码,并按B重新编码。那么Lua5.1读取后应该是不做任何转换的,作为字符串存储的依然是裸字节流(A方式编码),对其编码方式无任何感知。而输出到控制台时会将裸字节流交予OS,由OS来解释输出。我设想OS是按照GBK或是GB2312的方式来解释输出的,换句话说这里中文字符串也是按GBK或是GB2312来编码的。于是在Lua5.1解释器下输出前三个字节:
1 print(string.format("%04x", string.byte("中华人民共和国", 1))) 2 print(string.format("%04x", string.byte("中华人民共和国", 2))) 3 print(string.format("%04x", string.byte("中华人民共和国", 3)))
结果为00d6,00d0,00bb。查了GBK编码表(http://www.qqxiuzi.cn/bianma/guobiaoma.php),“中”对应的编码为D6D0,确实是按照GBK之类编码的。如果是这样的话,那么修改编辑器的编码方式,再运行,如果OS还是按照GBK之类来输出的话,那么应该会出问题。于是在Notepad++中修改编码为utf8,运行输出为乱码,而前三个字节则变为了00e4,00b8,00ad,即e4b8ad,正好是“中”的UTF8编码(http://www.mytju.com/classcode/tools/encode_utf8.asp)。
那么Lua5.3又是如何呢?不幸的是,我是在Centos下测试的,其输出的前三个字节为e4b8ad,与“中”的UTF8编码相符,而中文字符串也能正常地输出。这样看来似乎没有问题了,Lua5.3确实可以读取中文字符串并正确输出。但是Lua5.3在windows下又是如何表现的呢?我编译了Lua5.3的win解释器,运行test.lua(强制以UTF8格式重新编码),其输出竟也与Lua5.1一样,中文乱码,前三个字节为“中”的UTF8编码。这么说来,问题应该在于Centos控制台输出时是默认按照utf8来解释的了,而windows则默认按GBK输出。具体原因暂时不再深究,等下一次再解决。
这期间又顺便看了JsonFx库在序列化结构体的字符串成员时的处理,将Char转化为utf32的4个字节,以16进制形式拼接在'u'的后面,这样中文就完全以ASCII字符的样子整合在字符串中了:
1 int num = 0; 2 int length = value.Length; 3 this.Writer.Write('"'); 4 int num2; 5 for (int i = num; i < length; i = num2 + 1) 6 { 7 char c = value[i]; 8 bool flag2 = c <= 'u001f' || c >= 'u007f' || c == '<' || c == '"' || c == '\'; 9 if (flag2) 10 { 11 bool flag3 = i > num; 12 if (flag3) 13 { 14 this.Writer.Write(value.Substring(num, i - num)); 15 } 16 num = i + 1; 17 char c2 = c; 18 switch (c2) 19 { 20 case '': 21 this.Writer.Write("\b"); 22 goto IL_192; 23 case ' ': 24 this.Writer.Write("\t"); 25 goto IL_192; 26 case ' ': 27 this.Writer.Write("\n"); 28 goto IL_192; 29 case 'v': 30 break; 31 case 'f': 32 this.Writer.Write("\f"); 33 goto IL_192; 34 case ' ': 35 this.Writer.Write("\r"); 36 goto IL_192; 37 default: 38 if (c2 == '"' || c2 == '\') 39 { 40 this.Writer.Write('\'); 41 this.Writer.Write(c); 42 goto IL_192; 43 } 44 break; 45 } 46 this.Writer.Write("\u"); 47 this.Writer.Write(char.ConvertToUtf32(value, i).ToString("X4")); 48 }
而反序列化过程则相反,拿到u后面4个字节,然后作为Unicode的4个字节转化为对应的Char:
1 case 'u': 2 { 3 int utf; 4 bool flag = this.index + 4 < this.SourceLength && int.TryParse(this.Source.Substring(this.index + 1, 4), NumberStyles.AllowHexSpecifier, NumberFormatInfo.InvariantInfo, out utf); 5 if (flag) 6 { 7 JsonReader.builder.Append(char.ConvertFromUtf32(utf)); 8 this.index += 4; 9 }
搞清楚这些,对于Json使用中碰到的Unicode转化问题,应该就能迎刃而解了。