Chinaunix首页 | 论坛 | 博客
  • 博客访问: 47150
  • 博文数量: 17
  • 博客积分: 720
  • 博客等级: 军士长
  • 技术积分: 157
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-15 11:23
文章分类

全部博文(17)

文章存档

2011年(4)

2010年(13)

我的朋友

分类:

2010-08-23 18:59:15

字符编码基础知识 - Unicode,UCS,GBK,GB2312,UTF-8

最近遇到一个UnicodeUTF-8关系比较的问题,之前在处理中文显示时也遇到过类似的问题,于是花时间学习了一下,在此做个总结归纳,借以加深理解。(本文多数内容均来自互联网,特此申明。)

一、相关概念

1UCS/ISO 10646

ISO组织制定的国际标准ISO 10646定义了通用字符集 (Universal Character Set, UCS)UCS是所有其他字符集标准的一个超集,它保证与其他字符集是双向兼容的。就是说,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码, 你不会丢失任何信息。

ISO 10646定义了一个31位的字符集。然而,在这巨大的编码空间中,迄今为止只分配了前 65534个码位 (0x0000 0xFFFD)。这个UCS16位子集称为基本多语言面 (Basic Multilingual Plane, BMP)。将被编码在16BMP以外的字符都属于非常特殊的字符(比如象形文字),且只有专家在历史和科学领域里才会用到它们。

UCS不仅给每个字符分配一个代码,而且赋予了一个正式的名字。表示一个UCSUnicode 值的十六进制数,通常在前面加上"U+",就象U+0041代表字符"拉丁大写字母A"UCS字符U+0000U+007FUS-ASCII(ISO 646)是一致的,U+0000U+00FFISO 8859-1(Latin-1) 也是一致的。从U+E000U+F8FF,以及BMP以外的大范围的编码是为私用保留的。

2UCS的编码格式

       UCS-2,用2byte表示一个字符,即可以表示BMP内的全部字符。

UCS-4,用4byte表示一个字符(实际上只用了31位,最高位必须为0),可以表示所有UCS字符(0~0x10FFFF)。

UCS-22^16=65536个码位,UCS-42^31=2147483648个码位。

UCS -4根据最高位为0的最高字节分成2^7=128group。每个group再根据次高字节分为256plane。每个plane根据第3个字节分为 256 (rows),每行包含256cells。当然同一行的cells只是最后一个字节不同,其余都相同。

group 0plane 0被称作基本多语言面(Basic Multilingual Plane),BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP

UCS-4BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。

3Unicode

       Unicode协会制定的编码机制,要将全世界常用文字都函括进去。在1.0中是16位编码, U+0000U+FFFF。在2.0开始抛弃了16位限制,原来的16位作为基本位平面(BMP),另外增加了16个位平面,相当于20位编码,编码范围00x10FFFF

4UTFUCS/Unicode Transformation Format

1UTF-8就是以8bit为单元对UCS/Unicode进行编码。从UnicodeUTF-8编码方式如下。

Unicode

UTF-8

U-00000000 - U-0000007F:

0xxxxxxx

U-00000080 - U-000007FF:

110xxxxx 10xxxxxx

U-00000800 - U-0000FFFF:

1110xxxx 10xxxxxx 10xxxxxx

U-00010000 - U-001FFFFF:

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

U-00200000 - U-03FFFFFF:

111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

U-04000000 - U-7FFFFFFF:

1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

xxx 的位置由字符编码数的二进制表示的位填入,越靠右的 x 具有越少的特殊意义。只用最短的那个足够表达一个字符编码数的多字节串。注意在多字节串中,第一个字节的开头"1"的数目就是整个串中字节的数目。

例如字的Unicode编码是6C496C490800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001 用这个比特流依次代替模板中的x,得到:11100110  10110001  10001001,即E6 B1 89 

       UTF-8有以下一些特点:

  • CPU字节顺序无关,可以在不同平台之间交流。
  • 容错能力高,任何一个字节损坏后,最多只会导致一个编码码位损失,不会链锁错误(GB码错一个字节就会整行乱码)
  • UCS 字符U+0000U+007F (ASCII)被编码为字节0x000x7F (ASCII兼容)。这意味着只包含 7 ASCII字符的文件在ASCIIUTF-8两种编码方式下是一样的。
  • 所有>U+007FUCS字符被编码为一个多个字节的串,每个字节都有标记位集。因此,ASCII字节(0x00-0x7F)不可能作为任何其他字符的一部分。
  • 表示非ASCII字符的多字节串的第一个字节总是在0xC00xFD的范围里,并指出这个字符包含多少个字节。多字节串的其余字节都在0x80 0xBF 范围里。这使得重新同步非常容易,并使编码无国界,且很少受丢失字节的影响。
  • 可以编入所有可能的2^31UCS代码
  • UTF-8 编码字符理论上可以最多到6个字节长,然而16BMP字符最多只用到3 字节长,正式的Unicode定义字符(U+0000 to U+10FFFF)最多占用4个字节
  • Bigendian UCS-4字节串的排列顺序是预定的。
  • 字节0xFE0xFFUTF-8编码中从未用到。

2UTF-16:以16bit为单元对UCS/Unicode进行编码,对于小于0x10000UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于 0x10000UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4BMP必然小于0x10000,所以就目前而言,可以认为UTF-16UCS-2基本相同。另外,UTF-16是变长码(2个或4byte),且以双字节作为编码单位所以实际存储、传输时与CPU字序有关,因此java有区分UTF-16BEUTF-16LE两种charset

3UTF-32:仅使用了Unicode范围(00x10FFFF)32位编码,相当于UCS-4的子集。

5、中文国际编码

字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的BIG5

GB2312(1980)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FEGB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。

ASCII GB2312GBK,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312GBK都属于双字节字符集 (DBCS)

2000 年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说,GB18030GB13000.120902个汉字的基础上增加了CJK(中日韩)扩展A6582个汉字(Unicode0x3400 – 0x4db5),一共收录了27484个汉字。

GB18030 的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A6582个汉字。例如:UCS0x3400GB18030中的编码应该是8139EF30UCS0x3401GB18030中的编码应该是8139EF31

GB 18030是中国所有非手持/嵌入式计算机系统的强制实施标准。

二、需要注意的关系

1UnicodeUCS

       ISOUnicode协会是两个不同的组织,都试图设计统一字符集,ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0ISO的最新标准是ISO 10646-3:2003

       总结起来,UnicodeISO 10646是两个不同的标准,但定义的内容基本相同(BMP部分完全相同),所以基本可以理解为UCS = Unicode

2、字符集与编码格式

       ASCIIGB2312GBKUCSUnicode这些都是字符集。字符集定义了字符编码表,即规定了整数到字符的对应关系,但并没有规定与这些整数如何表示、保存、传输,这些是由编码格式规定的。例如字的UCS/Unicode编码是6C49,你可以用4ascii数字来传输、保存这个编码;也可以用 utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8UTF-7UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF“UCS/Unicode Transformation Format”的缩写。

3big endianlittle endian

big endian little endianCPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。如果将49写在前面,就是little endian

我们一般将endian翻译成“字节序”,将big endianlittle endian称作“大尾”和“小尾”。

三、问题回答

1UnicodeUTF什么关系?UTF-8UTF-16的区别是什么,为什么会有不同的UTF编码,分别适用于什么场景?

UnicodeUTF的关系不用多说,一个是字符集,一个是编码方式。

UTF-16我理解更适合程序在内存中表示字符、操作字符,因为每个字符都是2个字节的,便于cpu处理,效率高。

UTF-8更适合作为存储和网络传输的编码方式,是由其特点决定的,一是容错性好;二是每个字符的字节数可以由第一个字节开头的“1”的个数确定,方便判断字符的结束;三是UTF-8是以字节为单位编码的,不存在与cpu相关的字节序的问题;四是UTF-8处理ASCII字符有优势,只用1个字节,省空间,当然这个优势在处理中文等多字节字符时是没有的。

2、使用Windows记事本的“另存为”,可以在GBKUnicodeUnicode big endianUTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?  我很早前就发现UnicodeUnicode big endianUTF-8编码的txt文件的开头会多出几 个字节,分别是FFFE Unicode,FEFFUnicode big endian,EFBBBFUTF-8)。但这些标记是基于什么标准呢? 

UTF -8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如 “奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是 “乙”?

Unicode规范中推荐的标记字节顺序的方法是BOMBOM不是“Bill Of Material”的BOM表,而是Byte Order MarkBOM是一个有点小聪明的想法:

UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFEUCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符”ZERO WIDTH NO-BREAK SPACE”。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM

UTF -8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是 EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

Windows就是使用BOM来标记文本文件的编码方式的。

阅读(933) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~