谈Symbian中的编码

转自:《八位,十六位,傻傻分不清楚》

 

其实不管搞什么平台,这些所谓的编码都是基础知识,应该尽可能多了解一点。除去平台相关性之外,这个东东都是通用的。

·Unicode
Symbian OS的内码采用了unicode,最直观地来说就是,想要让一个字符正确地显示在界面上,你得用unicode编码来告诉操作系统,你想显示的是哪个字符。假如你用GBK编码来告诉OS的话,对不起,它不认识,它会给你一个乱码。
unicode全称是Universal Multiple-Octet Coded Character Set,简称UCS,可以简单理解为Unicode Character Set的缩写。UCS有UCS-2和UCS-4之分,区别在于是用2个字节还是4个字节来表示一个字符。现在一般只用到UCS-2。
UCS-2有2^16=65536个码位,即可以表示65536个字符;UCS-4有2^31=2147483648个码位(为什么不是2^32?因为 UCS-4的最高位规定了必须为0)。
UCS-4最高字节的最大值为2^7==128,这个最高字节把UCS-4分成了128个groups,每个group再根据次高字节分成了256个 planes,每个plane根据第三个字节分为256个rows,每个row包含256个cells。2147483648个字符就是这样被层层分布的。
group0的plane0,被称为“Basic Multilingual Plane”,即BMP(此BMP可非微软的bmp位图哦)。显而易见,BMP只有最后两个字节能用来表示码位(即0x 00 00 00 00 ~ 0x 00 00 FF FF),换句话说,BMP和UCS-2等价。UCS-4编码中,将BMP去掉前面两个零字节,就得到了UCS-2。目前的UCS-4编码表中,貌似还没有任何字符被分配在BMP之外,也就是说,目前Unicode还只能表示少于65536个字符,说得更直白的话,就是目前只要用UCS-2就能搞定 unicode了。

·UTF
UCS只规定了一个字符如何编码,对于UCS-2来说,一个unicode字符如何被传输或者存储呢?这就是见仁见智的事了。为什么这么说呢?举个例子:
比如,“天朝”的“朝”字,unicode编码为0x 67 1D,二进制表示为0110 0111 0001 1101。为了用unicode告诉别人“朝”字,我该怎么表达呢?直接把0110 0111 0001 1101这个比特流通过网络传给别人吗?还是作一定的变化后告诉别人呢?
显然原样传或者变换传都是可行的,只要接收方知道我是怎么变换的即可。而这种变换的规则,就是UTF,全称是UCS Transformation Format。
被广泛接受的方案有UTF-8、UTF-7和UTF-16。其中最常用的是UTF-8和UTF-16。
先说说UTF-16。
先直接拷贝一份网络上前人的解释:
“UTF-16以16位为单元对UCS进行编码。对于小于0×10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于 0×10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0×10000,所以就目前而言,可以认为 UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。”
以上一段话表达了两层意思:1.目前来说,UTF-16可以原样表达所有的unicode字符 2.UTF-16要考虑字节序
说到字节序,待会儿会提到BOM的概念。不过现在还是先讲完UTF再说。

接下来说说UTF-8。UTF-8是个很聪明的方案,不像UTF-16那样直白地把UCS-2编码直接拿来用,它作了一点变化。规则如下:
UCS-2编码(Hex) UTF-8字节流(Bin)
0000 - 007F 0xxxxxxx(占用1个字节)
0080 - 07FF 110xxxxx 10xxxxxx (占用两个字节)
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx (占用三个字节)
———————————————
以上表格如何使用,举个例子。“USA”的UCS-2编码依次为:0x 00 55,0x 00 53,0x 00 41
用二进制表示依次是 0000 0000 0101 0101,0000 0000 0101 0011,0000 0000 0100 0001
这三个英文字符都落在规则的第一行,那么可以套用规则:
‘U’:0加上“0000 0000 0101 0101”的后七位101 0101–>0101 0101
‘S’:0加上“0000 0000 0101 0011”的后七位101 0011–>0101 0011
‘A’:0加上“0000 0000 0100 0001”的后七位100 0001–>0100 0001
显然,对于ASCII字符,其UTF-8就是其ASCII码
再来看中文的情况,“朝”字的UCS-2为0x 67 1D,对照上表,落在了第三行。则进行如下转换:
0x 671D–>0110 0111 0001 1101–>按照1110xxxx 10xxxxxx 10xxxxxx模板依次填入,得到:
11100110 10011100 10011101
测试一下对不对?打开windows notepad,填入“朝”字,保存,选择编码为UTF-8。接着用PsPad以十六进制模式打开,
可以看到为:
EFBB BFE6 9C9D
抛开前面的EFBBBF不看,后面依次是E6 9C 9D,用二进制表示的话,正好就是11100110 10011100 10011101。
事实上,汉字都是落在上表的第三行规则中,也就是说,汉字都是占用三个字符的。
两个字节的情况呢?小弟不知道哪些字符是占两个字节的,不过显然是存在的。

然后是UTF-7,貌似用得不是那么多,估计也是和UTF-8那样用某种规则替换即可。这里不作深究。

到这里可以作个小总结了。传输英文文本,首选UTF-8,因为它等同于ANSI编码,每个英文字符都只占用1个字节,占用的比特流只有UTF-16 的一半。
对于中英文混排,UTF-8和UTF-16在体积上就不好说了。

·BOM
接下来说说字节序的问题。我们知道一个UCS-2编码占用两个字节,那么在以UTF-16存储或者传输的时候,先到的那个字节,到底是低字节还是高字节呢?
比如:著名的“天朝”的“朝”字,0×671D,先收到0×1D,后收到0×67,你在解释这两个字节时,到底是解释成0×671D呢,还是 0×1D67?显然,如果没有约定的话,还真不好办。
因为0×1D67也是客观存在的一个合法unicode字符呀。
此时就用到BOM了。BOM是Byte Order Mark的缩写。
在UCS编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的UCS-2编码是0xFEFF,这个字符是不可见的(因为它是zero width的,汗- -“`),也就是描述不出来的,因此不应该在实际使用中出现。
于是这个苦命的字符就被用来当做BOM了。在开始传输UTF-16码流之前,先传一个这个字符,如果用户接收到FEFF,表示此次传输的UTF-16码流是高字节在前的,即Big-Endian(大端),简写作:
UTF-16 BE,反过来,如果用户接收到FFFE,则表示此次传输的UTF-16码流是低字节在前的,即Little-Endian(小端),简写作:UTF-16 LE

对于UTF-8来说,实际上是没有字节序的问题的,为什么呢?
我们以单字节为单位来解释UTF-8码流,假如收到的是0xxxxxxx,那么就是UCS-2中的ASCII字符,假如收到的是110xxxxx,那么这个字节的后五个bit连同接下来一个字节的后六个bit,应该被组合起来作为一个UCS-2字符。
假如收到的是1110xxxx,那么这个字节的后4个bit,组合下一个以及下下个字节的后6bit,就作为一个UCS-2字符来解释。

虽然UTF-8不需要BOM,但是也可以加上BOM来直观地表示这是一段UTF-8码流。
windows平台下就追加了EFBBBF三个字节作为BOM。这就是上文中跳过EFBBBF的理由了。

以上把unicode大概讲了一下,对付日常使用,已经足够啦。
接下来结合,来讲讲文件、字符编码相关的应用。

在论坛里,问得比较频繁的一类问题是:“请问应该用什么编码读取txt文件?”“请问写入txt应该以什么方式写入”之类。
其实如果认真读了上文,就会发现,这类问题都问得不够精准,而且让回答者比较郁闷,怎么回答好呢,是完完整整从头讲吗?貌似不是三言两语能讲完的。是凭日常经验讲吗?这样又很容易误导人家。

对于文件读取或者写入,首先要明白的一个观点是:可以用RFile以字节为单位进行读/写,也可以用流,在高一层次上以16位双字节为单位进行读 /写。选择使用哪种,取决于你。
从这个角度来说,就是对于8位还是16位的读写选择。
对于字符编码,可以选择UTF-8作为存储方式,也可以直接选择UTF-16 LE作为存储方式。(为什么不用BE,呃,这个问题嘛,貌似arm cpu都是LE的,所以为了不自找麻烦,还是使用LE吧)
从这个角度来说,又是一个8位16位的抉择。
两个8位16位的出现,导致了不少新手GG/MM们纠结不断,傻傻分不清楚。
其实这两个完全是两回事,用UTF-8编码,完全可以用16位描述符来封装数据,然后通过流写入。读取则相反,通过流读取到16位描述符的缓冲中,然后当做8位UTF-8码流来解释。
同理,用UTF-16编码,可以用RFile按字节写入,也完全可以用流将16位描述符写入。
关键是用哪种对你来说最方便而已。
而对于UTF-8码流和UTF-16码流的互转,Symbian OS也内置了API,可以很方便地转换,相信大家都知道,在此不作复述。

不过在这里,又涉及到另一个纠结之处:8位描述符和16位描述符的互转。哈哈,真的是傻傻分不清楚了。
两者其实有本质不同,从实际应用习惯来说,UTF-8码流一般用8位描述符包装,而16位描述符里面一般装的都是UTF-16码流。
码流间的互转是改变物理数据的一种转换,而描述符之间的互转,可能改变物理数据,可能只是封装形式的不同,其内部所hold的物理数据,却没有发生变化。
具体地说:
case 1:
8bit的描述符中,hold了UTF-8码流,要转换成UTF-16的话,应该用16bit描述符作为最终接收者
使用EscapeUtils进行转码

case 2:
8bit的描述符中,hold了UTF-16码流,这段UTF-16码流其实已经就位了,因为其物理数据是OK的,但是还不能直接使用
必须用一个TPtrC16来封装一下,使用IMPORT_C TPtrC16::TPtrC16(const TUint16 *aBuf, TInt aLength); 这个构造函数来搞吧。
只是我有点疑问,用户是怎么获得一个hold了UTF-16码流的8bit的描述符的?嘿嘿

case 3:
16bit的描述符中,hold了UTF-8码流。想要在程序中直接使用的话,显然需要转换成UTF-16码流。
但是看看 static IMPORT_C HBufC* EscapeUtils::ConvertToUnicodeFromUtf8L(const TDesC8 &aData);
貌似不接受16bit的描述符诶,没关系,我们用IMPORT_C TPtrC8::TPtrC8(const TUint8 *aBuf, TInt aLength); 这个构造搞个TPtrC8出来就行啦。
不过这里又要遇到一个费解的问题了,为什么用户会有一个16bit的hold了UTF-8码流的描述符呢?呵呵,理论上可行,但是实际情况不太会出现。

case 4:
16bit的描述符中,hold了UTF-16的码流。 这是最OK的情况了,直接用吧,呵呵。

好了,以上4个case已经涵盖了可能的情况了。切忌对于case 2和case 3,不要想当然地用TDes::Copy()来拷贝数据,从8bit到16bit,会高字节置零,而从16bit到8bit,高字节又会被丢弃。

OK,写完收工!

转载于:https://www.cnblogs.com/candyboy/archive/2010/04/09/1708036.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值