`

编码问题

 
阅读更多
 1、

  基本概念

  bit 位只能是0或者1

  byte 字节一个字节是8位,1 byte=8 bits 计算机表示的基本单位

  KB,MB,GB,TB,PB是以1024与byte进行换算

  进制 用符号进行计数 十进制、二进制、八进制(011)、十六进制(0xFF)

  字符文字和符号的总称

  字符集 多个字符集合的总称。ASCII字符集、GB2312字符集、GBK字符集、BIG5字符集、GB18003字符集、Unicode字符集

  byte可表示2^8=256个字符的表示

  0 0×00 0000,0000

  1 0×01 0000,0001

  2 0×01 0000,0010

  127 0×7F 0111,1111

  -128 0×80 1000,0000

  -2 0xFE 1111,1110

  -1 0xFF 1111,1111

  以补码的形式表示的二进制编码。

  -2的表示,2=0000,0010,反码1111,1101,补码=反码+1=11111110

  1111,1110表示的就是1111,1110-1=1111,1101,取反就是0000,0010也就是2,所以就是-2

  2、

  字符集和编码

  2.1字符(Character)

  字符(Character)是文字与符号的总称,包括文字、图形符号、数学符号等。

  2.2字符集(Character Set)

  一组抽象字符的集合就是字符集(Character Set)。字符集常常和一种具体的语言文字对应起来,该文字中的所有字符或者大部分常用字符就构成了该文字的字符集,比如英文字符集。一组有共同特征的字符也可以组成字符集,比如繁体汉字字符集、日文汉字字符集。字符集的子集也是字符集。

  计算机要处理各种字符,就需要将字符和二进制内码对应起来,这种对应关系就是字符编码(Encoding)。制定编码首先要确定字符集,并将字符集内的字符排序,然后和二进制数字对应起来。根据字符集内字符的多少,会确定用几个字节来编码。每种编码都限定了一个明确的字符集合,叫做被编码过的字符集 (Coded Character Set),这是字符集的另外一个含义。通常所说的字符集大多都是指编码字符集(Coded Character Set)。

  1

  2.2.1 ASCII字符集

  ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于罗马字母表的一套电脑编码系统。由美国国家标准局(ANSI)制定。

  7位,可以表示2^7=128个字符。在计算机的存储单元中,一个ASCII码值占一个字节(8个二进制位),其最高位(b7)用作奇偶校验位。 7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符对ASCII进行了扩展,ASCII扩展字符集使用8位(bits)表示一个字符,共 256字符。

  ASCII扩展字符集比ASCII字符集扩充出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号。

  2.2.2 GB2312 字符集

  GB2312又称为GB2312-80字符集,全称为《信息交换用汉字编码字符集·基本集》,由原中国国家标准总局发布,1981年5月1日实施。在中国大陆和新加坡获广泛使用。GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。其中包括6763个汉字,其中一级汉字3755个,二级汉字3008个;包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

  GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。各区包含的字符如下:01-09区为特殊符号;16-55区为一级汉字,按拼音排序;56-87区为二级汉字,按部首/笔画排序;10-15区及88-94区则未有编码。

  两个字节中前面的字节为第一字节,后面的字节为第二字节。习惯上称第一字节为“高字节” ,而称第二字节为“低字节”。“高位字节”使用了0xA1-0xF7 (把01-87区(88-94区未有编码)的区号加上0xA0),“低位字节”使用了0xA1-0xFE (把01-94加上0xA0)。

  以GB2312字符集的第一个汉字“啊”字为例,它的区号16,位号01,则区位码是1601,在大多数计算机程序中,高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是:0xB0=0xA0+16, 0xA1=0xA0+1。

  2.2.3 GBK 字符集

  GBK全名为汉字内码扩展规范,英文名Chinese Internal Code Specification。K 即是“扩展”所对应的汉语拼音(KuoZhan11)中“扩”字的声母。GBK 来自中国国家标准代码GB 13000.1-93。GBK: 汉字国标扩展码,基本上采用了原来GB2312-80所有的汉字及码位,并涵盖了原Unicode中所有的汉字20902,总共收录了883个符号, 21003个汉字及提供了1894个造字码位。[(GBKH-0xB0)*0x5E+(GBKL-0xA1)]*(汉字离散后每个汉字点阵所占用的字节)

  GBK是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。另外,GBK中还包含繁体字的编码。

  GBK中每个汉字仍然包含两个字节,第一个字节的范围是0×81-0xFE(即129-254),第二个字节的范围是0×40-0xFE(即 64-254)。GBK中有码位23940个,包含汉字21003个。

  2

  2.2.4 BIG5字符集

  又称大五码或五大码,1984年由台湾财团法人信息工业策进会和五间软件公司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立,故称大五码。Big5码的产生,是因为当时台湾不同厂商各自推出不同的编码,如倚天码、IBM PS55、王安码等,彼此不能兼容;另一方面,台湾政府当时尚未推出官方的汉字编码,而中国大陆的GB2312编码亦未有收录繁体中文字。

  Big5字符集共收录13,053个中文字,该字符集在中国台湾使用。耐人寻味的是该字符集重复地收录了两个相同的字:“兀”(0xA461及 0xC94A)、“嗀”(0xDCD1及0xDDFC)。

  Big5码使用了双字节储存方法,以两个字节来编码一个字。第一个字节称为“高位字节”,第二个字节称为“低位字节”。高位字节的编码范围 0xA1-0xF9,低位字节的编码范围0×40-0×7E及0xA1-0xFE。各编码范围对应的字符类型如下:0xA140-0xA3BF为标点符号、希腊字母及特殊符号,另外于0xA259-0xA261,存放了双音节度量衡单位用字:兙兛兞兝兡兣嗧瓩糎;0xA440-0xC67E为常用汉字,先按笔划再按部首排序;0xC940-0xF9D5为次常用汉字,亦是先按笔划再按部首排序。





  2.2.5 GB18030字符集

  GB 18030的全称是GB18030-2000《信息交换用汉字编码字符集基本集的扩充》,是我国政府于2000年3月17日发布的新的汉字编码国家标准,2001年8月31日后在中国市场上发布的软件必须符合本标准。GB 18030-2000收录了27533个汉字,GB 18030-2005收录了70244个汉字。GB18030的总编码空间超过150万个码位。

  GB 18030字符集标准解决汉字、日文假名、朝鲜语和中国少数民族文字组成的大字符集计算机编码问题。该标准的字符总编码空间超过150万个编码位,收录了 27484个汉字,覆盖中文、日文、朝鲜语和中国少数民族文字。满足中国大陆、香港、台湾、日本和韩国等东亚地区信息交换多文种、大字量、多用途、统一编码格式的要求。并且与Unicode 3.0版本兼容,填补Unicode扩展字符字汇“统一汉字扩展A”的内容。并且与以前的国家字符编码标准(GB2312,GB13000.1)兼容。

  GB 18030标准采用单字节、双字节和四字节三种方式对字符编码。单字节部分使用0×00至0×7F码(对应于ASCII码的相应码)。双字节部分,首字节码从0×81至0×FE,尾字节码位分别是0×40至0×7E和0×80至0×FE。四字节部分采用GB/T 11383未采用的0×30到0×39作为对双字节编码扩充的后缀,这样扩充的四字节编码,其范围为0×81308130到0×FE39FE39。其中第一、三个字节编码码位均为0×81至0×FE,第二、四个字节编码码位均为0×30至0×39。

  双字节部分收录内容主要包括GB13000.1全部CJK汉字20902个、有关标点符号、表意文字描述符13个、增补的汉字和部首/构件80个、双字节编码的欧元符号等。四字节部分收录了上述双字节字符之外的,包括CJK统一汉字扩充A在内的GB 13000.1中的全部字符。

  2.2.6ANSI编码

  不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各

  3

  自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。”DBCS”(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

  汉字编码范围

  名称 第一字节 第二字节

  GB2312 0xB0-0xF7(176-247) 0xA0-0xFE(160-254)

  GBK 0×81-0xFE(129-254) 0×40-0xFE(64-254)

  Big5 0×81-0xFE(129-255) 0×40-0×7E(64-126)或者0xA1-0xFE(161-254)

  2.3 字符集编码(Character Set Encoding)

  ASCII,GB2312,GBK,BIG5,GB18030, UCS,Utf-8,utf-16,utf-32 都有自己不同的规则,都有自己的对应规则,但都兼容ASCII。在使用时要注意这些编码相互之间的转换规则。对于没有转换规则的编码体系之间进行转换只能依靠查编码表进行。

  2.4 ISO的编码体系

  2.4.1 ASCII编码

  ASCII的编号是ISO-646。

  2.4.2 ISO8859编码

  ISO 8859,全称ISO/IEC 8859,是国际标准化组织(ISO)及国际电工委员会(IEC)联合制定的一系列8位字符集的标准,现时定义了17个字符集。

  * ISO 8859-1 (Latin-1) – 西欧语言

  * ISO 8859-2 (Latin-2) – 中欧语言

  * ISO 8859-3 (Latin-3) – 南欧语言。世界语也可用此字符集显示。

  * ISO 8859-4 (Latin-4) – 北欧语言

  * ISO 8859-5 (Cyrillic) – 斯拉夫语言

  * ISO 8859-6 (Arabic) – 阿拉伯语

  * ISO 8859-7 (Greek) – 希腊语

  * ISO 8859-8 (Hebrew) – 希伯来语(视觉顺序)

  * ISO 8859-8-I – 希伯来语(逻辑顺序)

  * ISO 8859-9 (Latin-5 或 Turkish) – 它把Latin-1的冰岛语字母换走,加入土耳其语字母。

  * ISO 8859-10 (Latin-6 或 Nordic) – 北日耳曼语支,用来代替Latin-4。

  * ISO 8859-11 (Thai) – 泰语,从泰国的 TIS620 标准字集演化而来。

  * ISO 8859-13 (Latin-7 或 Baltic Rim) – 波罗的语族

  * ISO 8859-14 (Latin-8 或 Celtic) – 凯尔特语族

  * ISO 8859-15 (Latin-9) – 西欧语言,加入Latin-1欠缺的法语及芬兰语重

  4

  音字母,以及欧元符号。

  * ISO 8859-16 (Latin-10) – 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。

  2.4.3ISO10046(UCS)编码与Unicode

  UCS :

  通用字符集(Universal Character Set,UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的字符编码方式,采用4字节编码。

  Unicode:

  Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。

  它是http://www.unicode.org 制定的编码机制, 要将全世界常用文字都函括进去。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。但自从unicode2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码,ISO也承诺ISO10646将不会给超出0×10FFFF的UCS-4编码赋值,使得两者保持一致。Unicode的编码方式与ISO 10646的通用字符集(Universal Character Set,UCS)概念相对应,目前的用于实用的Unicode版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节,基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。

  UTF:

  Unicode 的实现方式不同于编码方式。

  一个字符的Unicode编码是确定的,但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。

  UTF-8: 8bit变长编码,对于大多数常用字符集(ASCII中0~127字符)它只使用单字节,而对其它常用字符(特别是朝鲜和汉语会意文字),它使用3字节。

  UTF-16: 16bit编码,是变长码,大致相当于20位编码,值在0×0000到

  0×10FFFF之间,基本上就是Unicode编码的实现,与CPU字序有关。

  UTF-32:32bit编码,定长编码对应于字符的Unicode表示。

  Unicode big endia:

  在Windows系统中保存文本文件时通常可以选择编码为ANSI、Unicode、Unicode big endian和UTF-8,这里的ANSI和Unicode big endia是什么编码呢?

  UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。

  Unicode规范中推荐的标记字节顺序的方法是BOM(即Byte Order Mark)。

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

  如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,

  5

  就表明这个字节流是Little-Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。Windows就是使用BOM来标记文本文件的编码方式的。





  2.5 codepage的编码体系

  codepage 指的是一个经过挑选的以特定顺序排列的字符内码列表,对于早期的单字节内码的语种,codepage中的内码顺序使得系统可以按照此列表来根据键盘的输入值给出一个对应的内码.对于双字节内码,则给出的是MultiByte到Unicode的对应表,这样就可以把以Unicode形式存放的字符转化为相应的字符内码.类似unicode,只是另外一种字符编码方式,注意ASP和SAP中的codepage的区别。

  ASP中:

  CodePage的作用,是决定页面以何种编码方式显示动态内容。当页面被服务器处理之后,页面将以CodePage设定的编码输出到客户端。当然,CodePage的参数需正确,否则,将产生错误信息“CodePage 值无效。指定的 CodePage 值无效。”(事件ID: 0204)。如果CodePage没有设置,则服务器使用默认的CodePage加载到你的Session里面,使用程序代码: Response.Write(Session.CodePage)可以查看你当前使用的CodePage。

  SAP中:最经常我们使用的读取数据的方法就是使用GUI_UPLOAD这个FM.在这个FM中有个CODEPAGE,是用来指定代码页的.

  Siebel Value

  SAP Code page

  Description

  CP1252

  1100

  SAP Latin-1 – ISO8859-1 – code page

  ISO-8859-2

  1402

  SAP Latin-2 – ISO8859-2

  ISO-8859-5

  1500

  SAP Cyrillic – ISO8859-5

  CP1254

  1610

  SAP Turkish – ISO8859-9

  CP1253

  1700

  SAP Greek – ISO8859-7 – Not a complete match

  CP1255

  1800

  SAP Hebrew – ISO8859-8 – Not a complete match

  CP932

  8000

  6

  SAP Shift-JIS

  CP950

  8300

  SAP Taiwanese

  CP936

  8400

  SAP Chinese

  CP949

  8500

  SAP Korean

  CP874

  8600

  SAP Thai

  3、

  Unicode历史

  1991年,Unicode联盟与ISO的工作组终于开始讨论Unicode与UCS的合并问题。最终,两者统一了抽象字符集(即任何一个在 Unicode中存在的字符,在UCS中也存在),对于码空间,两者同意以一百一十万为限,Unicode将码空间扩展到了一百一十万,而UCS将永久性的不使用一百一十万以后的码位。UCS和Unicode都指的是编码字符集,而不是字符集编码。

  字符集编码决定了如何将一个字符的整数编号对应到一个二进制的整数值,有的编码方案简单的将该整数值直接作为其在计算机中的表示而存储,例如英文字符就是这样,几乎所有的字符集编码方案中,英文字母的整数编号与其在计算机内部存储的二进制形式都一致。当初Unicode与UCS还没成家之时,UCS也是需要人爱,需要人疼的,没有自己的字符集编码怎么成。UCS-2与UCS-4就扮演了这样的角色。 UCS-4与UTF-32除了名字不同以外,思想完全一样。而UCS-2与UTF-16在对前65536个字符的处理上也完全相同,唯一的区别只在于 UCS-2 不支持surrogate pair机制,即是说,UCS-2只能对前65536个字符编码,对其后的字符毫无办法。

  4、

  Unicode的编码形式

  4.1 Unicode字符集

  Unicode字符集编码是Universal Multiple-Octet Coded Character Set 通用多八位编码字符集的简称,是由一个名为 Unicode 学术学会(Unicode Consortium)的机构制订的字符

  编码系统,支持现今世界各种不同语言的书面文本的交换、处理及显示。该编码于1990年开始研发,1994年正式公布,最新版本是2005年3月31日的 Unicode 4.1.0。Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,例如字母“A”的编码为 004116,所以“A”的编码书写为“U+0041”。

  7

  现在的Unicode码空间为U+0000到U+10FFFF,一共1114112个码位,其中只有1,112,064 个码位是合法的(我来替你做算术,有2048个码位不合法),但并不是说现在的Unicode就有这么多个字符了,实际上其中很多码位还是空闲的,到 Unicode 4.0 规范为止,只有96,382个码位被分配了字符(但无论如何,仍比很多人认为的65536个字符要多得多了)。其中U+0000 到U+FFFF的部分被称为基本多语言面(Basic Multilingual Plane,BMP)。U+10000及以上的字符称为补充字符。在Java中(Java1.5之后),补充字符使用两个char型变量来表示,这两个 char型变量就组成了所谓的surrogate pair(在底层实际上是使用一个int进行表示的)。第一个char型变量的范围称为“高代理部分”(high-surrogates range,从”uD800到”uDBFF,共1024个码位), 第二个char型变量的范围称为low-surrogates range(从”uDC00到”uDFFF,共1024个码位),这样使用surrogate pair可以表示的字符数一共是1024的平方计1048576个,加上BMP的65536个码位,去掉2048个非法的码位,正好是1,112,064 个码位。

  关于Unicode的码空间实际上有一些稍不小心就会让人犯错的地方。比如我们都知道从U+0000到U+FFFF的部分被称为基本多语言面(Basic Multilingual Plane,BMP),这个范围内的字符在使用UTF-16编码时,只需要一个char型变量就可以保存。仔细看看这个范围,应该有65536这么大,因此你会说单字节的UTF-16编码能够表示65536个字符,你也会说Unicode的基本多语言面包含65536个字符,但是再想想刚才说过的 surrogate pair,一个UTF-16表示的增补字符(再一次的,需要两个char型变量才能表示的字符)怎样才能被正确的识别为增补字符,而不是两个普通的字符呢?答案你也知道,就是通过看它的第一个char是不是在高代理范围内,第二个char是不是在低代理范围内来决定,这也意味着,高代理和低代理所占的共 2048个码位(从0xD800到0xDFFF)是不能分配给其他字符的。但这是对UTF-16这种编码方法而言,而对Unicode这样的字符集呢?在 Unicode的编号中,U+D800到U+DFFF是否有字符分配?答案是也没有!这是典型的字符集为方便编码方法而做的安排(你问他们这么做的目的?当然是希望基本多语言面中的字符和一个char型的UTF-16编码的字符能够一一对应,少些麻烦,从中我们也能看出UTF-16与Unicode间很深的渊源与结合)。也就是说,无论Unicode还是UTF-16编码后的字符,在0×0000至0xFFFF这个范围内,只有63488个字符。这就好比最初的CPU被勉强拿来做多媒体应用,用得多了,CPU就不得不修正自己从硬件上对多媒体应用提供支持了。

  尽管不情愿,但说到这里总还得扯扯相关的概念:代码点和代码单元。代码点(Code Point)就是指Unicode中为字符分配的编号,一个字符只占一个代码点,例如我们说到字符“汉”,它的代码点是U+6C49.代码单元(Code Unit)则是针对编码方法而言,它指的是编码方法中对一个字符编码以后所占的最小存储单元。例如UTF-8中,代码单元是一个字节,因为一个字符可以被编码为1个,2个或者3个4个字节;在UTF-16中,代码单元变成了两个字节(就是一个 char),因为一个字符可以被编码为1个或2个char(你找不到比一个char还小的UTF-16编码的字符,嘿嘿)。说得再罗嗦一点,一个字符,仅仅对应一个代码点,但却可能有多个代码单元(即可能被编码为2个char)。以上概念绝非学术化的绕口令,这意味着当你想以一种统一的方式指定自己使用什么字符的时候,使用代码点(即你告诉你的程序,你要用Unicode中的第几个字符)总是比使用代码单元更好(因为这样做的话你还得区分情况,有时候提供一个16进制数字,有时候要提供两个)。

  8

  例如我们有一个增补字符???(哈哈,你看到了三个问号对吧?因为我的系统显示不出这个字符),它在Unicode中的编号是U+2F81A,当在程序中需要使用这个字符的时候,就可以这样来写:

  String s=String.valueOf(Character.toChars(0×2F81A));

  char[]chars=s.toCharArray();

  for(char c:chars){

  System.out.format(”%x”,(short)c);

  }

  后面的for循环把这个字符的UTF-16编码打印了出来,结果是d87edc1a注意到了吗?这个字符变成了两个char型变量,其中0xd87e就是高代理部分的值,0xdc1a就是低代理的值。

  Unicode字符集编码(Universal Multiple-Octet Coded Character Set)通用多八位编码字符集的简称,支持世界上超过650种语言的国际字符集。Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,字母“A”的编码书写为“U+0041”。

  Unicode计划使用了17个平面,一共有 17*65536=1114112个码位。在Unicode 5.0.0版本中,已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD和0×100000-0×10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为 PUA。平面0也有一个专用区:0xE000-0xF8FF,有6400个码位。平面0的 0xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域。代理区的目的用两个UTF-16字符表示BMP以外的字符。在介绍UTF-16编码时会介绍。如前所述在Unicode 5.0.0版本中,238605-65534*2-6400-2408=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面 14上,它们对应着Unicode目前定义的99089个字符,其中包括71226个汉字。平面0、平面1、平面2和平面14上分别定义了52080、 3419、43253和337个字符。平面2的43253个字符都是汉字。平面0上定义了27973个汉字。





  4.2 UCS编码

  早期的Unicode标准有UCS-2、UCS-4的说法。UCS-2用两个字节编码,UCS-4用4个字节编码。UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行(row),每行有 256个码位(cell)。group 0的平面0被称作BMP(Basic Multilingual Plane)。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。

  每个平面有2^16=65536个码位。

  4.3 UTF编码

  Unicode 标准的编码字符集的字符编码方案。UTF是 Unicode Translation Format,即把Unicode转做某种格式的意思。UTF-32、UTF-16 和 UTF-8 是Unicode 标准的编码字符集的字符编码方案,UTF-16 使用一个或两个未分配的

  9

  16位代码单元的序列对Unicode 代码点进行编码;UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数。

  在Unicode中:汉字“字”对应的数字是23383。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、 UTF-16、UTF-32。UTF是“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,“汉字”对应的数字是 0×6c49和0×5b57,而编码的程序数据是:

  BYTE data_utf8[] = {0xE6, 0xB1, 0×89, 0xE5, 0xAD, 0×97}; // UTF-8编码

  WORD data_utf16[] = {0×6c49, 0×5b57}; // UTF-16编码

  DWORD data_utf32[] = {0×6c49, 0×5b57}; // UTF-32编码

  这里用BYTE、WORD、DWORD分别表示无符号8位整数,无符号16位整数和无符号32 位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。“汉字”的UTF-8编码需要6个字节。“汉字”的 UTF-16编码需要两个WORD,大小是4个字节。“汉字”的UTF-32编码需要两个DWORD,大小是8个字节。根据字节序的不同,UTF-16可以被实现为UTF-16LE或UTF-16BE,UTF-32可以被实现为UTF-32LE或UTF-32BE。下面介绍UTF-8、UTF-16、 UTF-32、字节序和BOM。

  UTF-8

  UTF-8最多是使用3个字节来表示一个字符。但理论上来说,UTF-8最多需要用6字节表示一个字符。

  00000000-0000007F的字符,用单个字节来表示;

  00000080-000007FF的字符用两个字节表示(中文的编码范围)

  00000800-0000FFFF的字符用3字节表示

  UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:

  Unicode编码 ║ UTF-8 字节流(二进制)

  000000 – 00007F ║ 0xxxxxxx 7bit

  000080 – 0007FF ║ 110xxxxx 10xxxxxx 11bit

  000800 – 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 16bit

  010000 – 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21bit

  UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0×00-0×7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0×10FFFF也只有21位。

  例1:“汉”字的Unicode编码是0×6C49。0×6C49在 0×0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0×6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

  例2:Unicode编码0×20C30在0×010000-0×10FFFF之间,使用用4 字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0×20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。

  10

  UTF-16

  UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下:

  如果U<0×10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。

  如果U≥0×10000,我们先计算U’=U-0×10000,然后将U’写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

  为什么U’可以被写成20个二进制位?Unicode的最大码位是0×10ffff,减去 0×10000后,U’的最大值是0xfffff,所以肯定可以用20个二进制位表示。例如:Unicode编码0×20C30,减去0×10000后,得到0×10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。

  按照上述规则,Unicode编码0×10000-0×10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个 WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。

  为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate):

  D800-DB7F ║ High Surrogates ║ 高位替代

  DB80-DBFF ║ High Private Use Surrogates ║ 高位专用替代

  DC00-DFFF ║ Low Surrogates ║ 低位替代

  高位替代就是指这个范围的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORD的UTF-16编码的第二个WORD。那么,高位专用替代是什么意思?我们来解答这个问题,顺便看看怎么由UTF-16 编码推导Unicode编码。

  如果一个字符的UTF-16编码的第一个WORD在0xDB80到0xDBFF之间,那么它的 Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是 0xDB80 0xDC00到0xDBFF 0xDFFF。我们将这个范围写成二进制:

  1101101110000000 1101110000000000 – 1101101111111111 1101111111111111

  按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到

  1110 0000 0000 0000 0000 – 1111 1111 1111 1111 1111

  即0xe0000-0xfffff,按照编码的相反步骤再加上0×10000,得到0xf0000-0×10ffff。这就是UTF-16编码的第一个 WORD在0xdb80到0xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB80到0xDBFF之间的保留码位被称作高位专用替代。

  UTF-32

  UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。

  11

  字节序

  根据字节序的不同,UTF-16可以被实现为UTF-16LE或UTF-16BE,UTF-32可以被实现为UTF-32LE或UTF-32BE。例如:

  Unicode编码 ║ UTF-16LE ║ UTF-16BE ║ UTF32-LE ║ UTF32-BE

  0×006C49 ║ 49 6C ║ 6C 49 ║ 49 6C 00 00 ║ 00 00 6C 49

  0×020C30 ║ 43 D8 30 DC ║ D8 43 DC 30 ║ 30 0C 02 00 ║ 00 02 0C 30

  BOM

  那么,怎么判断字节流的字节序呢?Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符”零宽无中断空格”。这个字符的编码是FEFF,而反过来的FFFE(UTF- 16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。下表是各种UTF编码的BOM:

  UTF编码 ║ Byte Order Mark

  UTF-8 ║ EF BB BF

  UTF-16LE ║ FF FE

  UTF-16BE ║ FE FF

  UTF-32LE ║ FF FE 00 00

  UTF-32BE ║ 00 00 FE FF

  5、

  java中使用的Unicode

  5.1 内部编码

  Java中,字符只以一种形式存在,那就是JVM内部的内部表示,Unicode码编号(U+0000~U+10FFFF)。JVM的唯一确定一个字符使得一个编码在进入jvm或者从jvm输出时需要进行编码转换。也就是编码转换只发生在JVM和OS以及网络传输的交互地带,也就是IO的各种byte或者 reader/writer输入输出发生作用的地方。在JVM和OS以及网络流交互的时间,Reader和Writer只是适用默认编码进行了默认的编码转换,来转换为字符流。面向字符是指系统文件中的字符和内存中的要一致。而面向字节是要保证系统中的二进制内容和读入JVM内部的二进制内容要一致。

  5.2 utf-16

  总共17个平面

  0×0000~0×10FFFF 1114112-2048=1112064个码位

  Unicode已定义的码位是238605个

  平面15之定义了占65534个码位的专用区,0xF0000~0xFFFFD。

  平面16之定义了占65534个码位的专用区,0×100000~0×10FFFD。

  平面0中定义了6400个专有区,0xE0000~0xF8FF。

  238605-65534*2-6400-2408=99089余下的分布在平面0、1、2、14上。

  12

  平面0上定义了52080个字符;

  平面1上定义了3419个字符;

  平面2上定义了43253个字符;

  平面14上定义了337个字符。

  平面2的43253个字符都是汉字,平面0上定义了27973个汉字。

  基本平面 0×0000~0xFFFF

  1~14平面 0×10000~0xEFFFF 0xD800~0xDBFF高位 DCOO~DFFF低位

  15平面 0xF0000~0xFFFFF 0xDB80~0xDBBF高位 DCOO~DFFF低位

  16平面 0×100000~0×10FFFF 0xDBC0~0xDBFF高位 DCOO~DFFF低位





  5.3高位序列和低位序列的判断

  java.lang.String#getBytes(String) 的源代码

  java.lang.StringCoding.encode 的源代码,再通过其底层类库的字符集类sun.io.CharacterEncoding 可以找出 Unicode 的转换器,是采用sun.io.CharToByteUnicode 这个类的,这个类的 sun.io 包是读取file.encoding.pkg 这个系统属性拼接字符串反射而来的。

  6、

  常见问题

  6.1 通用UTF-8来编码

  大量使用国外的开源软件时,UTF-8才是编码界最通用的语言。对英文是单字节、中文是三字节。在大量的英文存在的情况下高效。

  6.2编码问题时查看

  %javahome%/jre/lib/charsets.jar

  6.3 语言的编码

  C、C++、Python2内部字符串都是使用当前系统默认编码。

  Python3、Java内部字符串用Unicode保存。

  Ruby有一个内部变量$KCODE用来表示可识别的多字节字符串的编码,变量值为”EUC” “SJIS” “UTF8″ “NONE”之一。$KCODE的值为”EUC”时,将假定字符串或正则表达式的编码为EUC-JP。同样地,若为”SJIS”时则认定为Shift JIS。若为”UTF8″时则认定为UTF-8。若为”NONE”时,将不会识别多字节字符串。在向该变量赋值时,只有第1个字节起作用,且不区分大小写字母。”e” “E” 代表”EUC”,”s” “S” 代表 “SJIS”,”u” “U” 代表 “UTF8″,而”n” “N” 则代表 “NONE”。默认值为”NONE”。即默认情况下Ruby把字符串当成单字节序列来处理。

  6.4 js的unicode

  <script type=”text/javascript”>

  document.write(String.fromCharCode(72,69,76,76,79));

  document.write(”<br />”);

  document.write(String.fromCharCode(65,66,67));

  </script>

  13

  6.5 网页编码

  一个网页要在浏览器中正常显示,需要保持网页文件的编码、网页的meta标签声明(charset 来制定的其实是encoding编码而不是字符集)、浏览器编码设置是一致的。

  6.6 联通乱码

  在Win下的新建一个记事本文件,输入”联通”两个字之后,保存之后重新打开,发现出现乱码。这是因为GBK编码与UTF8编码产生了编码冲突。

  从UNICODE到UTF8的转换规则:

  Unicode UTF-8

  0000 – 007F 0xxxxxxx

  0080 – 07FF 110xxxxx 10xxxxxx

  0800 – FFFF 1110xxxx 10xxxxxx 10xxxxxx

  联的Unicode编码是[0x80] [0x54]

  通的Unicode编码是[0x90] [0x1A]

  8054和901A在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。使用第三种转换得到

  [0xE8] [0x81] [0x94] [0xE9] [0x80] [0x9A],这就是其UTF8的编码。

  新建一个文本文件时,记事本的编码默认是ANSI, 中文的就是GBK编码,而,此时”联通”的内码是:[0xC1] [0xAA] [0xCD] [0xA8]

  C1 1100 0001

  AA 1010 1010

  CD 1100 1101

  A8 1010 1000

  其中联的两个字节、通的两个字节的起始部分的都是”110″和”10″,与UTF8规则里的两字节模板是一致,所以再次用记事本打开时,记事本误认为这是一个UTF8编码的文件。按照反编码得到UNICODE的0x 006A,和0×0368,0×0368这个字符什么也不是,这就是”联通”两个字的文件没有办法在记事本里正常显示的原因。如果多几个字的输入话,由于记事本检测到不是合格的uft-8编码的字节转而会采用GBK,乱码又不出现。

  6.7Google学习乱码

  http://www.google.cn/search?hl=zh-CN&newwindow=1&q=学习

  出现乱码。

  6.8 java 编译时的乱码

  对于不是平台默认编码的情况下,java源文件在编译时,需要指定源文件的编码,否则无法正常编译。

  1、对于win下默认的GBK编码

  C:\>javac SqlUtility.java

  C:\>javac -encoding GBK SqlUtility.java

  C:\>javac -encoding utf-8 SqlUtility.java

  14

  SqlUtility.java:24: 警告:编码 utf-8 的不可映射字符

  * ????????????????

  ^

  2、对于unicode的默认是utf-16

  C:\>javac SqlUtility.java

  SqlUtility.java:38: 非法字符: \0

  C:\>javac -encoding utf-16 SqlUtility.java

  3、对于utf-8的编码,win下需要删除文件头的二进制编码EFBBBF(因为它是由Unicode标准的FEFF,为了保证字节序而存在),并不是

  C:\>javac SqlUtility.java

  SqlUtility.java:1: 警告:编码 GBK 的不可映射字符

  锘?**

  ^

  SqlUtility.java:1: 非法字符: \65533

  锘?**

  ^

  1 错误

  1 警告

  C:\>javac -encoding utf-16 SqlUtility.java

  SqlUtility.java:1: 非法字符: \61371

  C:\>javac -encoding utf-8 SqlUtility.java

  SqlUtility.java:1: 非法字符: \65279

  ?/**

  ^

  1 错误

  注:删除EFBBBF之后的

  C:\>javac -encoding utf-8 SqlUtility.java

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics