计算机的符号编码有很多种,如经典的ASCII编码,Unicode编码,UTF-8编码,UTF-16编码,UCS-2编码,UCS-4编码等,其中
ASCII编码是最具代表性的,由一个Byte组成,表示了英文字符、数字和全部键盘的控制符号;Unicode编码是ASCII编码的发展,有两个
Byte表示一个字符,可以表示6万多个字符,由于英文字符只需要一个Byte表示即可,用Unicode编码就造成了浪费(当然表示中文时便比较合算
了),所以就引入了UTF-8编码。本文主要介绍UTF-8编码和适合中文的UTF-16编码(UTF-16的引入是由于6万多个空间对于汉字来说还是不
够使用)。
1.UTF-8编码
先来看一个有趣的例子:
在记事本中只输入 力量
两个字,保存后再次打开你发现了什么?(或者借用网上的微软与联通有仇的趣事,输入 联通
两个字试试),为什么会出现这种情况?这就是UTF-8和GB2312惹的祸!记事本默认的存储编码方式是ANSI编码,这种编码是会随着计算机语言版本
而改变,在中文版本下,它就是GB2312编码,查看国标:力的GB2312编码为:0xC1A6=11000001
10100110;量的GB2312编码为:0xC1BF=11000001
10111111;所以第一次存储之后,记事本中的二进制码为11000001 10100110 11000001
10111111,再次打开,记事本会先根据“标识头”来决定用什么编码方式打开和显示,它看到没有任何“标识头”(Unicode的“标识头”是
FFFE,UTF-8的“标识头”是EFBBBF,不过有些老的UTF-8编码没有该“标识头”,所以便在字节中添加了标识信息),它便检查文本内所有字
符的字节信息,它看到110***** 10****** 110***** 10******这种信息,110*****
10******是老的UTF-8编码的标识信息
记事本便用UTF-8编码打开和显示该文本,它先取出第一个110*****
10******格式的Unicode编码(即星号所组成Unicode码)00000000
01100110=0x0066,找到对应字符f,再取出第二个110***** 10******格式的Unicode编码00000000
01111111=0x007F,找到对应字符DEL,这自然不能正常显示,在UtralEdit中可以看到显示的是f
鞘卤镜南允痉绞接險tralEdit不同,它什么都没显示。
然后再输入 力量
两个字,保存后再次打开,这次力量显示正常,那又为什么?因为这次保存的编码方式就是UTF-8(因为它认为这是UTF-8编码格式),所以加入了“标识
头”EFBBBF,而且将后来输入的 力量
两个字编成UTF-8编码格式,(因为用UTF-8编码格式打开,所以用的是Unicode码,而不是GB2312码) 力
的Unicode码是:0x529B=01010010 10011011,编成UTF-8编码格式是:
11100101 10001010
10011011 三个字节, 量 的Unicode码是0x91CF=10010001 11001111,编成UTF-8编码是:11101001
10000111 10001111,而先前被认为是UTF-8格式的 力量 则不用
变化,自然还是显示不出来。从上可以看出UTF-8编码有固定的格式,而且字节数不定,可以表示1~6个字节,可以容纳0~31位,如:
1字节:0*******; 容纳7位
0x00~0x7F
2字节:110***** 10******;
容纳11位 0x80~0x07FF
3字节:1110**** 10****** 10******
容纳16位 0x0800~0xFFFF
4字节:11110*** 10****** 10******
10****** 容纳21位 0x010000~0x1FFFFF
5字节:111110**
10****** 10****** 10****** 10****** 容纳26位 0x200000~0x03FFFFFF
6字节:1111110* 10****** 10****** 10****** 10****** 10****** 容纳31位
0x04000000~0x7FFFFFFF
1.UTF-16编码
UTF-16编码对古文的研究有重要意义,因为中国文字大部份是两字节,有的是四字节(很多人可能没见过,方正楷体S-超大字符集(SIP)这个字体里面
存放的都是四字节的字模,可以从下
载相关字体),Unicode编码与UTF-16编码的转换关系如下:
(下面部分引用他人,已经不知道出处)
字符按照UTF-16进行编码的规则是:
1.Unicode值小于0x10000的用等于该值的16位整数来表示。
2.Unicode值介于0x10000和0x10FFFF之间的,用一个值介于0xD800和0xDBFF(在所谓的高8位区)的16位整数和值介于
0xDC00和0xDFFF(在所谓的低8位区)的16位整数来表示。
3.Unicode值大于0x10FFFF不能按照UTF-16进行编码。
注意:在0xD800和0xDFFF间的值是特别为UTF-16预留,所以不应该将任何字符的值指定为这个区间内的数值。
UTF-16编码
将某个字符的Unicode值转换为UTF-16的编码按照以下步骤进行。假设U是给Unicode值,小于x10FFFF。
1) 如果U
< 0x10000,U的编码就是无符号的十六位整数,值和其本身的值一样,处理结束。
2)
如果U等于或者小于0x10FFFF,则设U' = U -
0x10000。此时,U'一定小于或者等于0xFFFFF,也就是说,U'的值不会超过20位。
3)
分别初始化2个16位无符号的整数,W1和W2为0xD800和0xDC00。每个整数都有10位可以用来对字符进行编码,正好能容纳U'的20位。
4) 将U'的高10位分配给W1的低10位,将U'的低10位分配给W2的低10位,处理结束。
用数字来表示,第2步到第4步如下所示:
U' = yyyyyyyyyyxxxxxxxxxx
W1 = 110110yyyyyyyyyy
W2 =
110111xxxxxxxxxx
UTF-16解码
将单个字符从UTF-16解码为Unicode值的步骤如下。设W1为待解码文字中第一个16位的整数,设W2为跟在W1后的整数(如果有的话)。
1) 如果W1小于0xD800或者W1大于0xDFFF,字符的值U就是W1的值,处理结束。
2)
判断W1的值是否介于0xD800和0xDBFF之间。如果不是,那么顺序有误,而且用W1将不能解码出任何合法字符。处理结束。
3)
如果没有W2(也就是说,以W1结尾),或者虽然有W2,但不是介于0xDC00和0xDFFF之间,那么顺序同样有错。处理结束。
4)
建立一个20位的无符号整数U',将W1的低10位作为U'的高10位,将W2的低10位作为U'的低10位。
5)
将U'的值加上0x10000以得到字符U的值,处理结束。
在VC里面要输出四字节的字,可以使用TextOut(hdc,int,int,LPCTSTR,int),其中的LPCTSTR必须是UTF16编码,
最后个数是2个。当然可以使用DrawText函数输出。但是只能在WindowsXP或以上版本才能很好的支持,如果是Windows2000,那么需
要打补丁surreg。如果是Windows98,那么将不能使用,只有自己读字体文件,这里介绍一个很好的库FreeType2,用它可以实现读取字体
文件。
阅读(2616) | 评论(0) | 转发(0) |