基础知识 - 字符编码简介

字符编码简介

1、字节

  字节是计算机中存储数据的最小单位,一个字节有 8 个位(即二进制位,也叫 bit),能够表示 0~255 之间的任何一个数(即二进制的 00000000 到 11111111 之间)。你能够用字节表示任何东西,好比数字、字符、图像、音乐等,这取决于你如何解释这个字节。

2、ASCII 字符集

  在标准 ASCII 码中,用一个字节来表示不一样的字符,字节的最高位(也就是二进制代码的左边第一位)被用来作奇偶校验,因此只剩下 7 个位用来表示不一样的字符,这样能表示的字符范围就变成了 0~127 之间。其中 0~3一、127 这些数值被定义为控制字符,他们是不能显示的。32~126 这些数值被定义为下面的字符:

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy

  若是要在计算机中存储 ASCII 字符串,只须要将这些字符所对应的数值(用来表示字符的数值也叫作码点)按字节依次存放便可,读取的时候也只要按字节依次读出便可。ASCII 字符集没法表示汉字。

3、Latin1 字符集

  若是将标准 ASCII 码的最高位不用做奇偶校验,而也用来表示字符的话,那么就能够表示 256 个字符,在 ASCII 的基础上多出了 128 个字符,这个字符集叫作 Latin1 字符集,Latin1 是 ISO-8859-1 的别名,也可写做 Latin-1。其中 0~127 这些数值的定义与 ASCII 字符集同样,128~159 这些数值被定义为控制字符,160~255 这些数值被定义为下面的字符:

 ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ

  若是要在计算机中存储 Latin1 字符串,只须要将这些字符所对应的码点(码点就是字符所对应的数值)按字节依次存放便可,读取的时候也只要按字节依次读出便可。Latin1 字符集没法表示汉字。

4、ANSI 字符集

  因为 ASCII 字符集或 Latin1 字符集只能表示有限的字符,对于亚洲字符(例如中文、日文、韩文)则没法表示,因此为了使计算机支持更多语言,出现了 ANSI 字符集,ANSI 字符集中 0~127 这些数值的定义与 ASCII 字符集同样,而 128~255 这些数值被定义为双字节字符的一个编码,即“用其中的 2 个数值来表示 1 个字符”,这样的双字节能够表示的字符数量就达到了 1.6 万多个。

  具体哪 2 个数值表明哪 1 个字符,则由各个国家本身去定义,不一样国家的 ANSI 字符集是不一样的,在中文系统下,ANSI 表明 GB2312 字符集,在日文系统下,ANSI 表明 JIS 字符集,不一样 ANSI 编码之间互不兼容,当信息在国际间交流时,没法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。

  因为 ANSI 字符串中的字符,有单字节和双字节之分,因此在存取的时候要判断当前字节是 0~127 范围的单字节,仍是 128~255 范围的双字节,并作相应处理,才能获得正确的字符,这样的判断和处理就称为编码和解码。编码是将字符(码点)转换为字节(单字节或双字节)保存起来,解码是将字节(单字节或双字节)还原为字符(码点)显示出来。

  ANSI 字符集存在的 BUG:当你在一个空文本文档中输入“联通”两个字,并保存为 ANSI 格式,再次打开的时候,内容将显示为乱码,这是由于,当文本文档中的全部的字符都知足“192 ≤ 第一字节 ≤ 223 且 128 ≤ 第二字节 ≤ 191”的时候,Windows 就没法正确识别文档的编码格式,错误的将 ANSI 格式识别成了 UTF-8 格式,形成解码错误,造成乱码。

  因此在保存文本文档的时候,不推荐使用 ANSI 编码格式保存,而应该使用更好的 UTF-8 或 Unicode 编码格式。

5、UCS 和 Unicode 字符集

  因为 ANSI 字符集的范围有限,不能包含世界上的全部字符,没法用于多语言环境(指可同时处理多种语言混合的状况),因此出现了 UCS 和 Unicode 字符集,UCS(Universal Character Set,通用字符集)使用 4 个字节来表示一个字符,其中最高字节的最高二进制位始终为 0,这样可以表示的字符数量就达到了约 20 亿个,足以容纳全世界全部的字符。在 UCS 字符集中,0~255 这些数值的定义与 Latin1 同样,只不过是用 4 个字节表示 1 个字符,除最低位字节外,其它 3 个字节所有用 0 填充。这样就保证了与 Latin1 字符集的兼容。

  UCS 是由 ISO 制定的 ISO 10646(或称 ISO/IEC 10646)标准所定义的标准字符集。UCS 将最高字节定义为 2^7=128 个组(group),每一个组再根据次高字节分为 256 个平面(plane),每一个平面再根据次低字节分为 256 行(row),每行再根据最低字节分为 256 个码位(cell)。UCS 中第 0 组的第 0 平面被称为“基本多文种平面”,里面存放了包含世界各国的经常使用字符。除了第 0 平面,UCS 还定义了 16 个辅助平面,用来存放更多的字符。

  Unicode 字符集是 UCS 字符集的子集,只包含 0x0~0x10FFFF 范围的字符。Unicode 3.0 及其如下版本只包含了“基本多文种平面”中的字符(即:0x0~0xFFFF),能够只使用 2 个字节来表示一个字符。Unicode 3.1 及其以上版本包含了 UCS 中的 16 个辅助平面(即:0x0~0x10FFF)。须要用 4 个字节来表示一个“基本多文种平面”之外的字符。

  “基本多文种平面”中有一个专用区“0xE000~0xF8FF”,有 6400 个码位,用来存放用户自定义的字符。还有一个被称做代理区(Surrogate)的特殊区域“0xD800~0xDFFF”,共 2048 个码位,代理区的目的是用两个 UTF-16 编码表示“基本多文种平面”之外的字符。具体参照“UTF16 编码”的介绍。

  历史上存在着两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟。前者开发 ISO/IEC 10646(UCS)项目,后者开发 Unicode 项目。所以最初制定了不一样的标准。1991 年先后,两个项目的参与者都认识到,世界不须要两个不兼容的字符集。因而,它们开始合并双方的工做成果,并为创立一个单一编码表而协同工做。从 Unicode 2.0 开始,Unicode 采用了与 ISO 10646-1 相同的字库和字码;ISO 也承诺,ISO 10646 将不会给超出 U+10FFFF 的 UCS-4 编码赋值,以使得二者保持一致。两个项目仍都存在,并独立地公布各自的标准。因为 Unicode 这一名字比较好记,于是它使用更为普遍。

6、Unicode 编码方式

  前面讲的 Unicode 字符集中的字符,只是单个的字符形式,若是要将多个 Unicode 字符保存到文件中,则须要一个编码方式,将 Unicode 码点转换为字节存放到文件中,目前经常使用的编码方式有:UTF-3二、UTF-1六、UTF-8 等。

  UTF 是“Unicode/UCS Transformation Format”的首字母缩写,即“把 Unicode 字符转换为某种格式”之意。

  UTF-32 对每个 Unicode 码点使用 4 个字节表示。UTF-32 足以表示 Unicode 全部组全部平面中的全部字符,可是咱们一般用不到那么多平面,咱们经常使用的只有“基本多文种平面”,对于这个平面中的字符而言,只用 2 个字节来表示就足够了,对于欧洲国家而言,一般只使用 ASCII 字符,若是使用 UTF-32 来保存,将浪费很大的存储空间。因此,UTF-32 在实际中用的并很少。

  UTF-16 对每个 Unicode 码点使用 2 个或 4 个字节表示,“基本多文种平面”内的字符使用 2 个字节足以所有表示出来,而“基本多文种平面”外的字符,则须要用到“代理区域(0xD800~0xDFFF)”,“代理区域”中的数值永久保留,不与 Unicode 字符进行映射。UTF-16 就利用保留下来的 0xD800~0xDFFF 中的数值来对“基本多文种平面”之外的字符进行编码。UTF-16 的编码规则以下:

  若是 Unicode 字符的码点在 0x0~0xFFFF 范围内,则直接用 Unicode 码点的最低 2 个字节做为其 UTF-16 的编码值。

  若是 Unicode 字符的码点在 0x01FFFF~0x10FFFF 范围内,则将该码点的数值减去 0x10000,而后将结果的二进制值(共 20 位)从中间分开成两部分(高 10 位和低 10 位),高 10 位的值(值的范围为 0x0~0x3FF)被加上 0xD800 获得第一个码元(或称做前导代理,值的范围是 0xD800~0xDBFF,占用 2 个字节)。低 10 位值(值的范围也是 0x0~0x3FF)被加上 0xDC00 获得第二个码元(或称做后尾代理,值的范围是 0xDC00~0xDFFF,占用 2 个字节)。这两个码元合在一块儿就构成了一个 4 字节的 UTF-16 编码,这样 UTF-16 就能够表示整个 Unicode 范围内的全部字符了。因而可知,UTF-16 是一种变长的编码方式,它的编码长度不固定,有些字符用 2 个字节表示,有些字符用 4 个字节表示。UTF-16 的编码方式也叫 Unicode 编码方式。

  UTF-8 对每个 Unicode 码点使用 一、二、三、4 个字节表示,也是一种变长的编码方式。

  对于 0x0~0x7F 之间的字符,使用 1 个字节表示,与 ASCII 字符集彻底相同。

  对于 0x80~0x7FF 之间的字符,使用 2 个字节表示,表示方法为,将该 Unicode 字符的二进制编码按顺序填入“110xxxxx 10xxxxxx”的 x 中便可获得 2 个字节的 UTF-8 编码。其中 110 是 UTF-8 编码的第一个字节标记,有了此标记,就说明该字节是一个 UTF-8 字符编码的开头字节,并且 110 中的 2 个 1 表示该 UTF-8 字符编码是由 2 个字节组成,后面 10 开头的字节表示 UTF-8 的后续字节。将这 2 个字节读出来,而后解码,就能够获得一个 Unicode 字符。

  对于 0x800~0xFFFF 之间的字符,使用 3 个字节表示,表示方法为,将该 Unicode 字符的二进制编码按顺序填入“1110xxxx 10xxxxxx 10xxxxxx”的 x 中便可获得 3 个字节的 UTF-8 编码。其中 1110 是 UTF-8 编码的第一个字节标记,有了此标记,就说明该字节是一个 UTF-8 字符编码的开头字节,并且 1110 中的 3 个 1 表示该 UTF-8 字符编码是由 3 个字节组成,后面 10 开头的字节表示 UTF-8 的后续字节。将这 3 个字节读出来,而后解码,就能够获得一个 Unicode 字符。

  对于 0x10000~0x10FFFF 之间的字符,使用 4 个字节表示,表示方法为,将该 Unicode 字符的二进制编码按顺序填入“11110xxx 10xxxxxx 10xxxxxx 10xxxxxx”的 x 中便可获得 4 个字节的 UTF-8 编码。其中 11110 是 UTF-8 编码的第一个字节标记,有了此标记,就说明该字节是一个 UTF-8 字符编码的开头字节,并且 11110 中的 4 个 1 表示该 UTF-8 字符编码是由 4 个字节组成,后面 10 开头的字节表示 UTF-8 的后续字节。将这 4 个字节读出来,而后解码,就能够获得一个 Unicode 字符。

7、BOM

  在 UTF-32 和 UTF-16 编码中,有些系统将编码的高位字节放前面,低位字节放后面,而有些系统则恰好相反,这就形成不一样系统之间,由于存放字节的顺序不一样,而互相不兼容的状况。为了解决这个问题,UCS 建议在整个 UTF-32 或 UTF-16 编码流的起始位置添加一个标记,用来标识该编码流的字节存放顺序。

  在 UCS 编码中有一个叫作“Zero Width No-Break Space”(中文译做“零宽无间断间隔”)的字符,它的编码是 FEFF。而另外一个编码 FFFE 在 UCS 中是不存在的字符,因此不该该出如今实际传输中。UCS 规范建议咱们在传输字节流前,先传输 BOM(Byte Order Mark,字节顺序标号)标记。这样若是接收者收到 FEFF,就代表这个字节流是 Big-Endian 格式(高位字节在后);若是收到 FFFE,就代表这个字节流是 Little- Endian 格式(低位字节在后)。所以像 FEFF、FFFE 这样的标记又被称做 BOM。

  在 Windows 和 Unix 系统中,UTF-32 和 UTF-16 使用的是 Little-Endian 字节顺序,而在 Mac 系统中则使用 Big-Endian 字节顺序。

  UTF-8 编码的字节顺序是固定的,在编码的时候就定好了。若是在 UTF-8 编码中使用 BOM(UTF-8 的 BOM 为 EFBBBF),则 BOM 只能用来标示它是一个 UTF-8 文件,而不用来讲明字节顺序。

8、总结

  字符集的概念比较简单,就是将数值与字符一一对映起来便可,该数值就是该字符的码点,好比 ASCII 字符集、Latin1 字符集、Unicode 字符集,一个数值对应一个字符。

  要将字符集中的多个字符保存到文件中,就不那么容易了,对于 ASCII、Latin1 这样的单字节字符集而言,保存到文件是比较简单的,将码点按顺序写入文件便可。而对于 Unicode 这样的多字节字符集而言,要保存到文件,就要考虑效率和空间利用率问题,既要高效率,又不能浪费太多空间,因此就出现了各类各样的 UTF 编码方式。

  对于欧洲国家而言,使用 UTF-8 编码保存文本比较合适,对于亚洲国家而言,使用 UTF-16 编码(即 Unicode 编码)保存文本比较合适。

  对于比较特殊的 ANSI 字符集,因为它的字符集在不一样的国家是不同的,各国之间互不兼容,并且容量比较小,仍是弃之不用比较好。



相关文章
相关标签/搜索