Weitere ähnliche Inhalte
Ähnlich wie Java中编码以及Unicode总结V1.1 (10)
Java中编码以及Unicode总结V1.1
- 1. Java 中编码以及 Unicode 总结
Zianed Hou
zianed@live.cn
1、基本概念
2、字符集和编码
2.1 字符(Character)
2.2 字符集(Character Set)
2.3 字符集编码(Character Set Encoding)
2.4 ISO的编码体系
2.5 codepage的编码体系
3、Unicode历史
4、Unicode的编码形式
4.1 Unicode字符集
4.2 UCS编码
4.3 UTF编码
5、java中使用的Unicode
5.1 内部编码
5.2 utf-16
5.3 高位序列和低位序列的判断
6、常见问题
6.1 通用UTF-8 来编码
6.2 编码问题时查看
6.3 语言的编码
6.4 js的unicode
6.5 网页编码
6.6 联通乱码
6.7Google学习乱码
6.8 java 编译时的乱码
Zianed Version 1.1 1
- 2. 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 0x00 0000,0000
1 0x01 0000,0001
2 0x01 0000,0010
127 0x7F 0111,1111
-128 0x80 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)。字符集常常和一种具体的语
言文字对应起来,该文字中的所有字符或者大部分常用字符就构成了该文字的字
符集,比如英文字符集。一组有共同特征的字符也可以组成字符集,比如繁体汉
字字符集、日文汉字字符集。字符集的子集也是字符集。
计算机要处理各种字符,就需要将字符和二进制内码对应起来,这种对应关
Zianed Version 1.1 2
- 3. 系就是字符编码(Encoding)。制定编码首先要确定字符集,并将字符集内的字符
排序,然后和二进制数字对应起来。根据字符集内字符的多少,会确定用几个字
节来编码。每种编码都限定了一个明确的字符集合,叫做被编码过的字符集
(Coded Character Set),这是字符集的另外一个含义。通常所说的字符集大多都是
指编码字符集(Coded Character Set)。
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 中的汉字的编码与
Zianed Version 1.1 3
- 4. GBK 中汉字的相同。另外,GBK 中还包含繁体字的编码。
GBK 中每个汉字仍然包含两个字节,第一个字节的范围是 0x81-0xFE(即
129-254),第二个字节的范围是 0x40-0xFE(即 64-254)
。GBK 中有码位 23940
个,包含汉字 21003 个。
2.2.4 BIG5 字符集
又称大五码或五大码, 1984 年由台湾财团法人信息工业策进会和五间软件公
司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立,故称
大五码。Big5 码的产生,是因为当时台湾不同厂商各自推出不同的编码,如倚
天码、IBM PS55、王安码等,彼此不能兼容;另一方面,台湾政府当时尚未推
出官方的汉字编码,而中国大陆的 GB2312 编码亦未有收录繁体中文字。
Big5 字符集共收录 13,053 个中文字,该字符集在中国台湾使用。耐人寻味
的是该字符集重复地收录了两个相同的字: “兀”(0xA461 及 0xC94A)、 “嗀”
(0xDCD1 及 0xDDFC)。
Big5 码使用了双字节储存方法,以两个字节来编码一个字。第一个字节称为
“高位字节” ,第二个字节称为“低位字节” 。高位字节的编码范围 0xA1-0xF9,
低位字节的编码范围 0x40-0x7E 及 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 个、双字节编码的
Zianed Version 1.1 4
- 5. 欧元符号等。四字节部分收录了上述双字节字符之外的,包括 CJK 统一汉字扩
充 A 在内的 GB 13000.1 中的全部字符。
2.2.6ANSI 编码
不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各
自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,
称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文
操作系统下,ANSI 编码代表 JIS 编码。"DBCS"(Double Byte Charecter Set 双
字节字符集) 。在 DBCS 系列标准里,最大的特点是两字节长的汉字字符和一字
节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处
理,必须要注意字串里的每一个字节的值,如果这个值是大于 127 的,那么就认
为一个双字节字符集里的字符出现了。
汉字编码范围
名称 第一字节 第二字节
GB2312 0xB0-0xF7(176-247) 0xA0-0xFE(160-254)
GBK 0x81-0xFE(129-254) 0x40-0xFE(64-254)
Big5 0x81-0xFE(129-255) 0x40-0x7E(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) - 希腊语
Zianed Version 1.1 5
- 6. * 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 欠缺的法语及芬兰语重
音字母,以及欧元符号。
* 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 将不会给超出 0x10FFFF 的 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 位编码,值在 0x0000 到
0x10FFFF 之间,基本上就是 Unicode 编码的实现,与 CPU 字序有关。
UTF-32:32bit 编码,定长编码对应于字符的 Unicode 表示。
Unicode big endia:
在 Windows 系统中保存文本文件时通常可以选择编码为 ANSI、Unicode、
Zianed Version 1.1 6
- 7. 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,
就表明这个字节流是 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
Zianed Version 1.1 7
- 8. 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
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 个字符编码,
对其后的字符毫无办法。
Zianed Version 1.1 8
- 9. 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”。
现在的 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 间很深的渊源与
Zianed Version 1.1 9
- 10. 结合)也就是说,
。 无论 Unicode 还是 UTF-16 编码后的字符, 0x0000 至 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 进制数字,有
时候要提供两个) 。
例如我们有一个增补字符???(哈哈,你看到了三个问号对吧?因为我的
系统显示不出这个字符) ,它在 Unicode 中的编号是 U+2F81A,当在程序中需要
使用这个字符的时候,就可以这样来写:
String s=String.valueOf(Character.toChars(0x2F81A));
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 和
,
0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以
简写为 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 个字符,
Zianed Version 1.1 10
- 11. 其中包括 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 使用一个或两个未分配的
16 位代码单元的序列对 Unicode 代码点进行编码; UTF-32 即将每一个 Unicode
代码点表示为相同值的 32 位整数。
在 Unicode 中:汉字“字”对应的数字是 23383。在 Unicode 中,我们有很多
方式将数字 23383 表示成程序中的数据,包括:UTF-8、 UTF-16、UTF-32。UTF
是“UCS Transformation Format”的缩写,可以翻译成 Unicode 字符集转换格式,
即怎样将 Unicode 定义的数字转换成程序数据。例如, “汉字”对应的数字是
0x6c49 和 0x5b57,而编码的程序数据是:
BYTE data_utf8[] = {0xE6, 0xB1, 0x89, 0xE5, 0xAD, 0x97}; // UTF-8 编码
WORD data_utf16[] = {0x6c49, 0x5b57}; // UTF-16 编码
DWORD data_utf32[] = {0x6c49, 0x5b57}; // 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 的字符用两个字节表示 (中文的编码范围)
Zianed Version 1.1 11
- 12. 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 的特点是对不同范围的字符使用不同长度的编码。 对于 0x00-0x7F 之
间的字符, UTF-8 编码与 ASCII 编码完全相同。 UTF-8 编码的最大长度是 4 个字
节。 从上表可以看出, 字节模板有 21 个 x,
4 即可以容纳 21 位二进制数字。 Unicode
的最大码位 0x10FFFF 也只有 21 位。
例 1: “汉”字的 Unicode 编码是 0x6C49。0x6C49 在 0x0800-0xFFFF 之
间,使用用 3 字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将 0x6C49 写成二进制
是:0110 1100 0100 1001, 用这个比特流依次代替模板中的 x,得到:11100110
10110001 10001001,即 E6 B1 89。
例 2:Unicode 编码 0x20C30 在 0x010000-0x10FFFF 之间,使用用 4 字节模
板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将 0x20C30 写成 21 位二进制数
字(不足 21 位就在前面补 0) 0010 0000 1100 0011 0000,用这个比特流依次
:0
代替模板中的 x, 得到: 即
11110000 10100000 10110000 10110000, F0 A0 B0 B0。
UTF-16
UTF-16 编码以 16 位无符号整数为单位。我们把 Unicode 编码记作 U。
编码规则如下:
如果 U<0x10000,U 的 UTF-16 编码就是 U 对应的 16 位无符号整数(为书
写简便,下文将 16 位无符号整数记作 WORD) 。
如果 U≥0x10000,我们先计算 U'=U-0x10000,然后将 U'写成二进制形
式:yyyy yyyy yyxx xxxx xxxx,U 的 UTF-16 编码(二进制)就是:
110110yyyyyyyyyy 110111xxxxxxxxxx。
为什么 U'可以被写成 20 个二进制位?Unicode 的最大码位是 0x10ffff,
减去 0x10000 后,U'的最大值是 0xfffff,所以肯定可以用 20 个二进制位表示。
例如:Unicode 编码 0x20C30,减去 0x10000 后,得到 0x10C30,写成二进制是:
0001 0000 1100 0011 0000。用前 10 位依次替代模板中的 y,用后 10 位依次替代
模板中的 x, 就得到: 即
1101100001000011 1101110000110000, 0xD843 0xDC30。
按照上述规则,Unicode 编码 0x10000-0x10FFFF 的 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 ║ 高位专用替代
Zianed Version 1.1 12
- 13. 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 , 按 照 编 码 的 相 反 步 骤 再 加 上 0x10000 , 得 到
0xf0000-0x10ffff。这就是 UTF-16 编码的第一个 WORD 在 0xdb80 到 0xdbff 之间
的 Unicode 编码范围, 即平面 15 和平面 16。 因为 Unicode 标准将平面 15 和平面
16 都作为专用区,所以 0xDB80 到 0xDBFF 之间的保留码位被称作高位专用替
代。
UTF-32
UTF-32 编码以 32 位无符号整数为单位。Unicode 的 UTF-32 编码就是其
对应的 32 位无符号整数。
字节序
根据字节序的不同,UTF-16 可以被实现为 UTF-16LE 或 UTF-16BE,
UTF-32 可以被实现为 UTF-32LE 或 UTF-32BE。例如:
Unicode 编码 ║ UTF-16LE ║ UTF-16BE ║ UTF32-LE ║
UTF32-BE
0x006C49 ║ 49 6C ║ 6C 49 ║ 49 6C 00 00 ║ 00 00 6C 49
0x020C30 ║ 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
Zianed Version 1.1 13
- 14. 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 个平面
0x0000~0x10FFFF 1114112-2048=1112064 个码位
Unicode 已定义的码位是 238605 个
平面 15 之定义了占 65534 个码位的专用区,0xF0000~0xFFFFD。
平面 16 之定义了占 65534 个码位的专用区,0x100000~0x10FFFD。
平面 0 中定义了 6400 个专有区,0xE0000~0xF8FF。
238605-65534*2-6400-2408=99089 余下的分布在平面 0、1、2、14 上。
平面 0 上定义了 52080 个字符;
平面 1 上定义了 3419 个字符;
平面 2 上定义了 43253 个字符;
平面 14 上定义了 337 个字符。
平面 2 的 43253 个字符都是汉字,平面 0 上定义了 27973 个汉字。
基本平面 0x0000~0xFFFF
1~14 平面 0x10000~0xEFFFF 0xD800~0xDBFF 高位 DCOO~DFFF 低位
15 平面 0xF0000~0xFFFFF 0xDB80~0xDBBF 高位 DCOO~DFFF 低位
16 平面 0x100000~0x10FFFF 0xDBC0~0xDBFF 高位 DCOO~DFFF 低位
5.3 高位序列和低位序列的判断
java.lang.String#getBytes(String) 的源代码
java.lang.StringCoding.encode 的 源 代 码 , 再 通 过 其 底 层 类 库 的 字 符 集 类
sun.io.CharacterEncoding 可 以 找 出 Unicode 的 转 换 器 , 是 采 用
Zianed Version 1.1 14
- 15. 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" 代表 "SJIS", "U" 代表 "UTF8",
"s" "u" 而"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>
Zianed Version 1.1 15
- 16. 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,和 0x0368,0x0368 这
个字符什么也不是, 这就是"联通"两个字的文件没有办法在记事本里正常显示的
原因。如果多几个字的输入话,由于记事本检测到不是合格的 uft-8 编码的字节
转而会采用 GBK,乱码又不出现。
6.7Google学习乱码
http://www.google.cn/search?hl=zh-CN&newwindow=1&q=学习
出现乱码。
Zianed Version 1.1 16
- 17. 6.8 java 编译时的乱码
对于不是平台默认编码的情况下,java 源文件在编译时,需要指定源文件的
编码,否则无法正常编译。
1、对于 win 下默认的 GBK 编码
C:>javac SqlUtility.java
C:>javac -encoding GBK SqlUtility.java
C:>javac -encoding utf-8 SqlUtility.java
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
Zianed Version 1.1 17