对Unicode的简单研究和了解 Version1.0

对Unicode的简单研究和了解
Version 1.0

    因为在写PHP的时候遇到过几次乱码问题,一次是使用Mysql查询语句的时候返回了一堆乱码,一次是使用fopen()函数时读取文件的时候乱码,或者使用fopen()时如果文件名是中文的话出现打不开的情况。
    对于上面讲的几种情况,使用Mysql时只要定义了你的语句使用了什么编码、以什么编码来处理、返回的结果是什么编码,如果你设定的那些编码和你网页中使用的编码一致,那么就不会有乱码的情况。另外,因为Windows记事本一般保存的是ANSI编码格式的文本,如果网页设定了使用UTF-8编码,那么在网页中打开一个文件当然是会出现乱码的。这时候就需要用到iconv()函数来对编码进行转换。
    那么,什么是Unicode,什么是UTF-8呢?
    我们平常讲的“Unicode编码”实际上是Unicode以UTF-16方式来使用的,之所以这样是因为UTF-16所使用的编码在0x0000-0xFFFF和Unicode里给每个字定义的名称是一致的,没有经过转换。Unicode实际上并不是编码,而是一个字符集。
    先来看看一个Unicode编码表:
     UNICODE 编码表

    下面我们就一起来详细地了解Unicode:
    一、Unicode的编码方式和实现方式:
    Unicode包括“编码方式”和“实现方式”两部分。以下对于这两个概念的说明部分来自维基百科:
    编码方式:unicode的编码方式与ISO 10646的通用字符集(Universal Character Set,UCS)概念相对应,目前实际应用的Unicode版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示216即65536个字符。基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。
上述16位Unicode字符构成基本多文种平面(Basic Multilingual Plane,简称BMP)。最新(但未实际广泛使用)的Unicode版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到ISO 10646-1实现级别3,即涵盖UCS-4的所有字符。UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。
BMP字符的Unicode编码表示为U+hhhh,其中每个h 代表一个十六进制数位。与UCS-2编码完全相同。对应的4字节UCS-4编码后两个字节一致,前两个字节的所有位均为0。我们再返回去看看上面的编码表:“他”对应的Unicode编码为“U+4ED6”。
实现方式:Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为UTF)。
例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大大节省了编码长度。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。
再如,如果直接使用与Unicode编码一致(仅限于BMP字符)的UTF-16编码,由于每个字符占用了两个字节,在Macintosh (Mac)机和PC机上,对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如某字符为十六进制编码4E59,按两个字节拆分为4E和59,在Mac上读取时是从低字节开始,那么在Mac OS会认为此4E59编码为594E,找到的字符为“奎”,而在Windows上从高字节开始读取,则编码为U+4E59的字符为“乙”。就是说在Windows下以UTF-16编码保存一个字符“乙”,在Mac OS里打开会显示成“奎”。此类情况说明UTF-16的编码顺序若不加以人为定义就可能发生混淆,于是在UTF-16编码实现方式中使用了大端序(Big-Endian, 简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE)的概念,以及可附加的字节顺序记号解决方案,目前在PC机上的Windows系统和Linux系统对于UTF-16编码默认使用UTF-16 LE。
此外Unicode的实现方式还包括UTF-7、Punycode、CESU-8、SCSU、UTF-32等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是UTF-16小尾序(LE)、UTF-16大尾序(BE)和UTF-8。在微软公司Windows XP操作系统附带的记事本(Notepad)中,“另存为”对话框可以选择的四种编码方式除去非Unicode编码的ANSI(对于英文系统即ASCII编码,中文系统则为GB2312或Big5编码) 外,其余三种为“Unicode”(对应UTF-16 LE)、“Unicode big endian”(对应UTF-16 BE)和“UTF-8”。
目前辅助平面的工作主要集中在第二和第三平面的中日韩统一表意文字中,因此包括GBK、GB18030、Big5等简体中文、繁体中文、日文、韩文以及越南喃字的各种编码与Unicode的协调性被重点关注。考虑到Unicode最终要涵盖所有的字符,从某种意义而言,这些编码方式也可视作Unicode的出现于其之前的既成事实的实现方式,如同ASCII及其扩展Latin-1一样,后两者的字符在16位Unicode编码空间中的编码第一字节各位全为0,第二字节编码与原编码完全一致。但上述东亚语言编码与Unicode编码的对应关系要复杂得多。


二、UTF-8和UTF-16显示汉字的实质
上面的Unicode编码表是大端序的编码,Windows使用的是小端序开始读取的,如果要显示一个“他”字的话要怎么办呢?
既然大端序的编码是“U+4ED6”,把它们两个两个地拆开,倒过来,就成了小端序“U+D64E”。
打开Ultraedit编辑器,新建一个文档,以16进制编辑,输入D64E,以UTF-16(无BOM)方式保存文档,再退出16进制编辑模式:
 UTF16
这样“他”就在屏幕上显示出来了。
上面那个是以UTF-16方式来输出“他”的,那么如果要以UTF-8方式来输出“他”要怎么办呢?
UTF-8和UTF-16不一样,UTF-8在0-0x7F范围之内和ASCII码表是相一致的,就是说在显示英文字符和其它半角符号的时候UTF-8只占用了一个字节,UTF-16占用了两个字节,比如显示一个“A”,UTF-8是0x41,而UTF-16则是0x0041。UTF-8和Unicode之间存在着以下关系:

Unicode编码                         UTF-8字节流(二进制)
0x000000-0x00007F           0xxxxxxx
0x000080-0x0007FF           110xxxxx 10xxxxxx
0x000800-0x00FFFF           1110xxxx 10xxxxxx 10xxxxxx
0x010000-0x10FFFF           11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

正如之前所说,它在0-0x00007F范围内和ASCII码表是相一致的,只占一个字节。其它字符可能会占2-3个字节。而我们平常使用的汉字占了多少字节呢?
我们回去看之前的“他”——U+4ED6,在0x000800-0x00FFFF范围之内,于是一个汉字占用的是三个字节。
我们把4ED6转换成二进制——100 111011 010110,将它们从低位开始向三个字节的模板进行填充,不足的在前面补0:
11100100 10111011 10010110
把它转换成16进制是0xE4 0xBB 0x96
Ultraedit新建一个文件,保存成UTF-8(无BOM)格式文件,以16进制方式编辑,输入E4 BB 96,再退出16进制编辑方式:
 UTF8
同样“他”也被显示出来了。
UTF-16在使用的时候需要注意小端序和大端序的问题,而UTF-8则不用。

【补充】这里出现了几个新概念:
1、 ISO10646:由“国际标准化组织(ISO)”制定的一个字符集,ISO和Unicode组织意识到世界上不应该出现两个不兼容的字符集,双方就开始了协调工作,它们为每个字所编的编码都是一致的,但也有差别,详细请百度= =
2、 UCS:通用字符集(Universal Character Set)。是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的字符编码方式。UCS不仅给每个字符分配一个代码,而且赋予了一个正式的名字。表示一个UCS或Unicode值的十六进制数通常在前面加上“U+”,例如“U+0041”代表字符“A”。(注:百度百科里给它解释的是“Unicode字符集”Unicode Character Set,但经过我考证这个好像是ISO制定的东西。另外UCS分为UCS-2和UCS-4,UCS-2采用两个字节,UCS-4采用4个字节。Unicode目前采用的是UCS-2,UCS-4是在UCS-2的前面补了两个0,UCS-4目前尚未普及)
3、 BOM:形象点说就是一个文件的“头”。这个“头”可以告诉系统这个文件是UTF-8的,还是UTF-16的等等。
4、 ASCII:[来自百度百科] ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。

    那么编写网页到底使用UTF-8好还是UTF-16好呢?
    如果你写的网页里有较多的英语,用UTF-8比较好,因为它在ASCII部分只占用一个字节的空间。而网页里有较多的中文、日文、韩文的时候用UTF-16就比较好了,因为在UTF-8里一个中文字符占用了三个字节的空间,UTF-16则只占用两个字节的空间。
    另外还有一个问题,在写网页的时候如果一个网页里只有英文字符,这时用无BOM的UTF-8格式保存文件,下次再打开的时候就变成了以ANSI编码保存的了。为什么呢?因为没有BOM的UTF-8文件在只有英文字符的情况下编码是和ANSI一致的。这时候如果返回了一个UTF-8格式的Mysql数据库记录可能会出现乱码。因此,在写网页的时候一定要在<head>里面写上META声明:
    <meta http-equiv=”content-type” content=”text/html;charset=utf-8” />
网页保存成什么格式并不重要,重要的是你要告诉浏览器这个网页的编码是什么。计算机也不会自动地猜对你的编码。
    举个例子:
    0x41 0xE4 0xBB 0x96
    你能说出它代表的是哪些字符吗?不能。除非我告诉你这个是什么编码格式的文件。在UTF-8里它代表“A他”,在UTF-16里它代表“銩柣”(假设它是个UTF-16 LE文件)。


三、其它注意事项
1、在编写PHP时不能够以UTF-8有BOM的格式保存文件,请选择以UTF-8无BOM格式保存。因为如果以有BOM格式保存的话文前头会多出EE BB BF,它在Unicode里面没有对应的内容,因此就用它来告诉计算机这个文件是UTF-8格式的。但是PHP不会去特殊处理这三个字节的内容,它会把它们输出到浏览器里,可能会显示成乱码,也可能会显示成空格。在include文件的时候很容易出错。
2、计算机怎么知道一个UTF-16格式的文件是大端序的文件还是小端序的呢?它们同样是通过BOM来实现的。
如果需要以大端序(UTF-16 BE)保存文件,保存为UTF-16格式的文件之后需要在最前面加上FE FF。
如果需要以小端序(UTF-16 LE)保存文件,保存为UTF-16格式的文件之后需要在最前面加上FF FE。

以上是我对Unicode的一点点了解和认识,一定有一些概念性或者理解上的错误,还请大家多多指正。另外还有什么需要补充的欢迎大家指点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值