ASCII字符集

7位的编码方案,总共表示128个字符,其中包括了大小写英文字母、数字、标点符号等常用字符。英语世界已经足够应付。

ISO-8859-1字符集

也称ISO-Latin字符集,它扩展了ASCII字符集,用到了8bit字节里的最高一位,这样它就有256个字符,前128个字符和ASCII字符集相同 。有了ISO-Latin字符集,西方世界的一些其它语言,如西班牙语、法语、德语、意大利语都够用了。

GB系列字符集(GB2312,GBK,GB18030)

由于一个字节是无论如何也表达不了哪怕是最长用的汉字字符集的,所以为了用计算机存储汉字,必须使用多个字节。

多字节字符集就是使用可变长的编码长度来编码字符,有的字符用一个字节编码,比如ASCII字符,有的字符用两个字节编码,比如汉字。在VC里,多字节字符集等同于双 字节字符集,VC不支持多于2个字节编码长度的字符。GB系列的字符集和ISO-Latin字符集一样,前128个字符和ASCII字符集相同。GB系列字符集是兼容 的,相同的中文字符在这3个字符集里有相同的编码。GB2312和GBK一个字符最多2个字节表示,GB18030可多达4个字节。在这种编码里表示汉字时,需要一个 leading byte,它总是大于127,这个字节的含义是说明它和后面的字节(们)一起表示一个字符。

这些字符集(ISO-Latin字符集,GB系列字符集)都是以ASCII为基础扩展而来,统称为ANSI字符集。

记事本在默认情况下(选择ANSI编码)就是使用多字节字符集保存文件的,至于使用的是GB2312,GBK,还是GB18030我不清楚。

Unicode字符集

每个地区的人都试图扩展ASCII编码来支持本地的语言,最终的结果是导致互不兼容。因为除了最低的128个字符相同以外,其它的字符都使用自己特殊的编码方案。

当使用与文件保存时的编码方案不同的编码来读取文件时,就会产生错误——比如Windows记事本那个著名的「联通BUG」。

统一所有字符的编码是Unicode被设计出来的初衷。

长久以来,Unicode在我心中的概念就是:使用2个字节来编码字符,使用Unicode可以表示世界上所有的字符。但这种理解并不准确!

其实Unicode可以看成是一种理想:这种理想就是世界上的所有字符都只有一个唯一的标识!至于怎样去实现这种理想,有很多的实现方式:UTF- 8,UTF-16,UTF-32,甚至在Unicode标准里还介绍了一种压缩的实现方式。Unicode把这个唯一的标识称之为代码点(code point),字符的代码点以U+XXXX的方式表示,这个可以打开Windows自带的字符映射表看得到。

Unicode最初被设计出来的时候希望使用2个字节就可以表示世界上的所有字符。因此,实现Unicode最直接的想法就是用两个字节来存储一个字符,如果大家都这 么想就好了,这样一个字符就可以用2个字节长的短整形来存储。但是偏偏还有一个叫做大端小端东西存在,这样2个字节的短整型在内存中的表示顺序就有2种可能,这就是为 什么当用记事本保存文本文件时可以选择Unicode或者Unicode big endian的原因。

1个字符=2个字节在现实中却遇到了麻烦。一方面,用2个字节表示一个字符,浪费了大量的空间(如果仅仅用来存储ISO- Latin字符集里的字符的话),而且还会有大端小端的问题,解决的方案是UTF- 8编码;另一方面,人们在实践中发现即使用2个字节编码也无法表示所有字符,因此出现了UTF- 16。UTF-16除了使用2个字节编码外,还使用一对2个字节来表 示Unicode里很少用到的字符;另外还有UTF-32,它使用单独的4个字节来编码所有的Unicode字符。

UTF-8编码

我想最早提出UTF-8的一定是美国人,「用2个字节来表示一个英语字母这太浪费了!」,他们肯定会这么说的。顾名思义,那个8说明UTF- 8编码中最小的单位是8bit的字节。采用UTF- 8编码,Unicode代码点中U+007F以下(包含U+007F)的字符用一个字节编码,其它的字符用多个字节编码,最多一个字符用4个字节编码。这样UTF- 8兼容ASCII,但是不兼容ISO-Latin字符集。

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

网上的很多文章都有这个表,可以看到一个Unicode代码点采用UTF-8编码时最多可达6个字节。但是从Unicode官方网站上看到的是UTF- 8编码的最大字节长度是4个字节。也就是说最下面的两行没有了。

UTF-8编码的实现方式比较好理解:例如「汉」字的Unicode编码是6C49,6C49在0800-FFFF之间,所以最终编码应该是3个字节。 6C49的二进制位串是:110110001001001,把这个位串从右向左填充到那3个字节的x部分,高位不够的用0补。最终得到的3个字节是:11100110 10110001 10001001,即E6 B1 89。注意由于UTF- 8的最小编码单元是字节,所以不存在大端小端的问题。在各种Unicode编码方案之间转换的标准算法(诸如从UTF-16到UTF- 8或者反过来)已经有了,在Unicode的官方网站上可以找到。

这样Unicode至少就有5种编码方案了(UTF-8,UTF-16两种,UTF-32两种),怎么区分它们呢?

区分各种不同Unicode编码方案的技巧被称为Byte Order Mark(BOM)

Byte order mark     Description

EF BB BF     UTF-8

FF FE     UTF-16, little endian

FE FF     UTF-16, big endian

FF FE 00 00     UTF-32

「像记事本、EditPlus这些软件,如果另存为unicode类型,会在文件最开头添加BOM码,如果一个文件没有BOM码,那么就很难识别,需要人工识别了。另 外,一般说Unicode编码,其实和Unicode-le(小头端)一个概念,Unicode Big endian会单独作为一种编码类型放置的。」-alswl


原文链接: https://blog.alswl.com/2009/09/encoding-ascii-ansi-gb-2312-unicode-utf-8-utf-16/
3a1ff193cee606bd1e2ea554a16353ee
欢迎关注我的微信公众号:窥豹

Comments

comments powered by Disqus