一个编码问题

    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 '\b':
21                             this.Writer.Write("\\b");
22                             goto IL_192;
23                         case '\t':
24                             this.Writer.Write("\\t");
25                             goto IL_192;
26                         case '\n':
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 '\r':
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转化问题,应该就能迎刃而解了。

 

posted on 2017-05-05 18:14  莫行  阅读(571)  评论(0编辑  收藏  举报

导航