unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian
ASCII(American Standard Code for Information Interchange)
美国信息交换标准代码,这是计算机上最早使用的通用的编码方案。那个时候计算机还只是拉丁文字的专利,根本没有想到现在计算机的发展势头,如果想到了,可能一开始就会使用 unicode 了。当时绝大部分专家都认为,要用计算机,必须熟练掌握英文。这种编码占用 7 个 Bit ,在计算机中占用一个字节, 8 位,最高位没用,通讯的时候有时用作奇偶校验位。因此 ASCII 编码的取值范围实际上是: 0x00-0x7f, 只能表示 128 个字符。后来发现 128 个不太够用,做了扩展,叫做 ASCII 扩展编码,用足八位,取值范围变成: 0x00-0xff, 能表示 256 个字符。其实这种扩展意义不大,因为 256 个字符表示一些非拉丁文字远远不够,但是表示拉丁文字,又用不完。所以扩展的意义还是为了下面的 ANSI 编码服务。
ANSI ( American National Standard Institite )
美国国家标准协会,也就是说,每个国家(非拉丁语系国家)自己制定自己的文字的编码规则,并得到了 ANSI 认可,符合 ANSI 的标准,全世界在表示对应国家文字的时候都通用这种编码就叫 ANSI 编码。换句话说,中国的 ANSI 编码和在日本的 ANSI 的意思是不一样的,因为都代表自己国家的文字编码标准。比如中国的 ANSI 对应就是 GB2312 标准,日本就是 JIT 标准,香港,台湾对应的是 BIG5 标准等等。当然这个问题也比较复杂,微软从 95 开始,用就是自己搞的一个标准 GBK 。 GB2312 里面只有 6763 个汉字, 682 个符号,所以确实有时候不是很够用。 GBK 一直能和 GB2312 相互混淆并且相安无事的一个重要原因是 GBK 全面兼容 GB2312 ,所以没有出现任何冲突,你用 GB2312 编码的文件通过 GBK 去解释一定能获得相同的显示效果,换句话说: GBK 对 GB2312 就是,你有的,我也有,你没得的,我还有!
好了, ANSI 的标准是什么呢,首先是 ASCII 的代码你不能用!也就是说 ASCII 码在任何 ANSI 中应该都是相同的。其他的,你们自己扩展。所以呢,中国人就把 ASCII 码变成 8 位, 0x7f 之前我不动你的,我从 0xa0 开始编, 0xa0 到 0xff 才 95 个码位,对于中国字那简直是杯水车薪,因此,就用两个字节吧,因此编码范围就从 0xA1A1 - 0xFEFE ,这个范围可以表示
23901 个汉字。基本够用了吧, GB2312 才 7000 多个呢! GBK 更猛,编码范围是从 0x8140 - 0xFEFE, 可以表示 3 万多个汉字。可以看出,这两种方案,都能保证汉字头一个字节在 0x7f 以上,从而和 ASCII 不会发生冲突。能够实现英文和汉字同时显示。 BIG5 ,香港和台湾用的比较多,繁体,范围: 0xA140 - 0xF9FE, 0xA1A1 - 0xF9FE ,每个字由两个字节组成,其第一
字节编码范围为 0xA1~0xF9 ,第二字节编码范围为 0x40-0x7E 与 0xA1-0xFE ,总计收入 13868 个字 ( 包括 5401 个常用字、 7652 个次常用字、 7 个扩充字、以及 808 个各式符号 ) 。
那么到底 ANSI 是多少位呢?这个不一定!比如在 GB2312 和 GBK , BIG5 中,是两位!但是其他标准或者其他语言如果不够用,就完全可能不止两位!
例如: GB18030:
GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的字形。 GBK2K 从根本上解决了字位不够,字形不足的问题。它有几个特点:它并没有确定所有的字形,只是规定了编码范围,留待以后扩充。编码是变长的,其二字节部分与 GBK 兼容;四字节部分是扩充的字形、字位,其编码范围是首字节 0x81-0xfe 、二字节
0x30-0x39 、三字节 0x81-0xfe 、四字节 0x30-0x39 。它的推广是分阶段的,首先要求实现的是能够完全映射到 Unicode3.0 标准的所有字形。它是国家标准,是强制性的。
搞懂了 ANSI 的含义,我们发现 ANSI 有个致命的缺陷,就是每个标准是各自为阵的,不保证能兼容。换句话说,要同时显示中文和日本文或者阿拉伯文,就完全可能会出现一个编码两个字符集里面都有对应,不知道该显示哪一个的问题,也就是编码重叠的问题。显然这样的方案不好,所以 Unicode 才会出现!
3.MBCS ( Multi-Byte Chactacter System ( Set) )
多字节字符系统或者字符集,基于 ANSI 编码的原理上,对一个字符的表示实际上无法确定他需要占用几个字节的,只能从编码本身来区分和解释。因此计算机在存储的时候,就是采用多字节存储的形式。也就是你需要几个字节我给你放几个字节,比如 A 我给你放一个字节,比如 " 中 “ ,我就给你放两个字节,这样的字符表示形式就是 MBCS 。
在基于 GBK 的 windows 中,不会超过 2 个字节,所以 windows 这种表示形式有叫做 DBCS ( Double-Byte Chactacter System ),其实算是 MBCS 的一个特例。
C 语言默认存放字符串就是用的 MBCS 格式。从原理上来说,这样是非常经济的一种方式。
4.CodePage
代码页,最早来自 IBM ,后来被微软, oracle ,SAP 等广泛采用。因为 ANSI 编码每个国家都不统一,不兼容,可能导致冲突,所以一个系统在处理文字的时候,必须要告诉计算机你的 ANSI 是哪个国家和地区的标准,这种国家和标准的代号(其实就是字符编码格式的代号),微软称为 Codepage 代码页,其实这个代码页和字符集编码的意思是一样的。告诉你代码页,本质就是告诉了你编码格式。但是不同厂家的代码页可能是完全不同,哪怕是同样的编码,比如, UTF-8 字符编码 在 IBM 对应的代码页是 1208 ,在微软对应的是 65001, 在德国的 SAP 公司对应的是 4110 。所以啊,其实本来就是一个东西,大家各自为政,搞那么多新名词,实在没必要!所以标准还是很重要的!!!
比如 GBK 的在微软的代码页是 936 ,告诉你代码页是 936 其实和告诉你我编码格式是 GBK 效果完全相同。那么处理文本的时候就不会有问题,不会去考虑某个代码是显示的韩文还是中文,同样,日文和韩文的代码页就和中文不同,这样就可以
避免编码冲突导致计算机不知如何处理的问题。当然用这个也可以很容易的切换语言版本。
但是这都是治标不治本的方法,还是无法解决同时显示多种语言的问题,所以最后还是都用 unicode 吧,永远不会有冲突了。
5.Unicode(Universal Code)
这是一个编码方案,说白了就是一张包含全世界所有文字的一个编码表,不管你用的上,用不上,不管是现在用的,还是以前用过的,只要这个世界上存在的文字符号,统统给你一个唯一的编码,这样就不可能有任何冲突了。不管你要同时显示任何文字,都没有问题。
因此在这样的方案下, Unicode 出现了。 Unicode 编码范围是: 0-0x10FFFF ,可以容纳 1114112 个字符, 100 多万啊。全世界的字符根本用不完了, Unicode 5.0 版本中,才用了 238605 个码位。所以足够了。
因此从码位范围看,严格的 unicode 需要 3 个字节来存储。但是考虑到理解性和计算机处理的方便性,理论上还是用 4 个字节来描述。
Unicode 采用的汉字相关编码用的是《 CJK 统一汉字编码字符集》 — 国家标准 GB13000.1 是完全等同于国际标准《通用多八位编码字符集 (UCS) 》 ISO 10646.1 。《 GB13000.1 》中最重要的也经常被采用的是其双字节形式的基本多文种平面。在这 65536 个码位的空间中,定义了几乎所有国家或地区的语言文字和符号。其中从 0x4E00 到 0x9FA5 的连续区域包含了 20902 个来自中国(包括台湾)、日本、韩国的汉字,称为 CJK (Chinese Japanese Korean) 汉字。 CJK 是《 GB2312-80 》、《 BIG5 》等字符集的超集。 CJK 包含了中国,日本,韩国,越南,香港,也就是 CJKVH 。这个在 UNICODE 的 Charset chart 中可以明显看到。 unicode 的相关标准可以从 unicode.org 上面获得,目前已经进行到了 6.0 版本。
下面这段描述来自百度百科:
Unicode 字符集可以简写为 UCS ( Unicode Character Set )。早期的 unicodeUnicode 标准有 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 个码位。 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 个字符,其中包括 71226 个汉字。平面 0 、平面 1 、平面 2 和平面 14 上分别定义了 52080 、 3419 、 43253 和 337 个字符。平面 2 的 43253 个字符都是汉字。平面 0 上定义了 27973 个汉字。
6.Unicode 的实现方案
Unicode 其实只是一张巨大的编码表。要在计算机里面实现,也出现了几种不同的方案。也就是说如何表示 unicode 编码的问题。
UTF-8 ( UCS Transformation Format 8bit)
这个方案的意思以 8 位为单位来标识文字,注意并不是说一个文字用 8 位标识。他其实是一种 MBCS 方案,可变字节的。到底需要几个字节表示一个符号,这个要根据这个符号的 unicode 编码来决定,最多 4 个字节。
编码规则如下:
Unicode 编码 (16 进制 ) ║ UTF-8 字节流 ( 二进制 )
000000 - 00007F ║ 0xxxxxxx
000080 - 0007FF ║ 110xxxxx 10xxxxxx
000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8 的特点是对不同范围的字符使用不同长度的编码。对于 0x00-0x7F 之间的字符, UTF-8 编码与 ASCII 编码完全相同。
UTF-8 编码的最大长度是 4 个字节。从上表可以看出, 4 字节模板有 21 个 x ,即可以容纳 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 ): 0 0010 0000 1100 0011 0000 ,用这个比特流依次代替模板中的 x ,得到: 11110000 10100000 10110000 10110000 ,即 F0 A0 B0 B0 。
( 2 ) UTF-16
UTF-16 编码以 16 位无符号整数为单位。注意是 16 位为一个单位,不表示一个字符就只有 16 位。现在机器上的 unicode 编码一般指的就是 UTF-16 。绝大部分 2 个字节就够了,但是不能绝对的说所有字符都是 2 个字节。这个要看字符的 unicode 编码处于什么范围而定,有可能是 2 个字节,也可能是 4 个字节。这点请注意!
下面算法解释来自百度百科。
我们把 Unicode 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 ║ 高位专用替代
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 11011100 00000000 - 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 之间的保留码位被称作高位专用替代。
( 3 ) UTF-32
这个就简单了,和 Unicode 码表基本一一对应,固定四个字节。
为什么不采用 UTF-32 呢,因为 unicode 定义的范围太大了,其实 99% 的人使用的字符编码不会超过 2 个字节,所以如同统一用 4 个字节,简单倒是简单了,但是数据冗余确实太大了,不好,所以 16 位是最好的。就算遇到超过 16 位能表示的字符,我们也可以通过上面讲到的代理技术,采用 32 位标识,这样的方案是最好的。所以现在绝大部分机器实现 unicode
还是采用的 utf-16 的方案。当然也有 UTF-8 的方案。比如 windows 用的就是 UTF16 方案,不少 linux 用的就是 utf8 方案。
7. 编码存储差异
这里就要引出两个名词:
LE ( little endian): 小字节字节序,意思就是一个单元在计算机中的存放时按照低位在前(低地址),高位在后(高地址)的模式存放。
BE ( big endian): 大字节字节序,和 LE 相反,是高位在前,低位在后。
比如一个 unicode 编码为: 0x006C49 ,如果是 LE ,那么在文件中的存放顺序应该是: 49 6c 00
如果是 BE , 那么顺序应该是: 00 6c 49
8. 编码格式的检测
到底采用什么编码,如果能检测就好了。专家们也是这么想的,所以专家给每种格式和字节序规定了一些特殊的编码,
这些编码在 unicode 中是没有使用的,所以不用担心会冲突。
这个叫做 BOM ( Byte Order Mark )头。意思是字节序标志头。通过它基本能确定编码格式和字节序。
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
所以通过检测文件前面的 BOM 头,基本能确定编码格式和字节序。
但是这个 BOM 头只是建议添加,不是强制的,所以不少软件和系统没有添加这个 BOM 头(所以有些软件格式中有带 BOM 头和 NoBOM 头的选择),这个时候要检测什么格式,就比较麻烦了当然可以检测,但是不能保证 100% 准确,只能通过编码范围从概率上来检查,虽然准确度还是比较高,但是不能保证 100% 。所以,时常看到检测错误的软件,也不奇怪了。
总结:
终于写完了,其实这些问题都是不统一导致的,属于历史问题,所以才会有这些困惑,这里也呼吁所有的软件 开发人员自觉的采用 Unicode 标准进行文字处理,我相信在不久的将来,这些困扰都不会存在了,因为所有软件都是 unicode d , 只要有字库,任何文字都能同时显示,也可以到任何语言的平台上的去运行,不再有乱码的困惑!其实现在绝大部分软件已经是这么做的了!
另外也不要被很多名词属于所迷惑,其实这些只是标准的问题,根本没有什么新的东西,更没有什么复杂的东西。
摘自 http://blog.csdn.net/softman11/archive/2011/01/08/6124345.aspx