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

 

posted on 2011-08-19 00:20 Vcer-JZ 阅读(397) 评论(0)  编辑 收藏 引用 所属分类: MFC


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

随笔分类

随笔档案

搜索

最新评论