zoukankan      html  css  js  c++  java
  • 基础知识 字符编码简介

    字符编码简介
    
    一、字节
    
      字节是计算机中存储数据的最小单位,一个字节有 8 个位(即二进制位,也叫 bit),可以表示 0~255 之间的任何一个数(即二进制的 00000000 到 11111111 之间)。你可以用字节表示任何东西,比如数字、字符、图像、音乐等,这取决于你如何解释这个字节。
    
    二、ASCII 字符集
    
      在标准 ASCII 码中,用一个字节来表示不同的字符,字节的最高位(也就是二进制代码的左边第一位)被用来做奇偶校验,所以只剩下 7 个位用来表示不同的字符,这样能表示的字符范围就变成了 0~127 之间。其中 0~31、127 这些数值被定义为控制字符,他们是不能显示的。32~126 这些数值被定义为下面的字符:
    
     !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxy
    
      如果要在计算机中存储 ASCII 字符串,只需要将这些字符所对应的数值(用来表示字符的数值也叫做码点)按字节依次存放即可,读取的时候也只要按字节依次读出即可。ASCII 字符集无法表示汉字。
    
    三、Latin1 字符集
    
      如果将标准 ASCII 码的最高位不用作奇偶校验,而也用来表示字符的话,那么就可以表示 256 个字符,在 ASCII 的基础上多出了 128 个字符,这个字符集叫做 Latin1 字符集,Latin1 是 ISO-8859-1 的别名,也可写作 Latin-1。其中 0~127 这些数值的定义与 ASCII 字符集一样,128~159 这些数值被定义为控制字符,160~255 这些数值被定义为下面的字符:
    
     ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
    
      如果要在计算机中存储 Latin1 字符串,只需要将这些字符所对应的码点(码点就是字符所对应的数值)按字节依次存放即可,读取的时候也只要按字节依次读出即可。Latin1 字符集无法表示汉字。
    
    四、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 编码格式。
    
    五、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 这一名字比较好记,因而它使用更为广泛。
    
    六、Unicode 编码方式
    
      前面讲的 Unicode 字符集中的字符,只是单个的字符形式,如果要将多个 Unicode 字符保存到文件中,则需要一个编码方式,将 Unicode 码点转换为字节存放到文件中,目前常用的编码方式有:UTF-32、UTF-16、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 码点使用 1、2、3、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 字符。
    
    七、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 文件,而不用来说明字节顺序。
    
    八、总结
    
      字符集的概念比较简单,就是将数值与字符一一对映起来即可,该数值就是该字符的码点,比如 ASCII 字符集、Latin1 字符集、Unicode 字符集,一个数值对应一个字符。
    
      要将字符集中的多个字符保存到文件中,就不那么容易了,对于 ASCII、Latin1 这样的单字节字符集而言,保存到文件是比较简单的,将码点按顺序写入文件即可。而对于 Unicode 这样的多字节字符集而言,要保存到文件,就要考虑效率和空间利用率问题,既要高效率,又不能浪费太多空间,所以就出现了各种各样的 UTF 编码方式。
    
      对于欧洲国家而言,使用 UTF-8 编码保存文本比较合适,对于亚洲国家而言,使用 UTF-16 编码(即 Unicode 编码)保存文本比较合适。
    
      对于比较特殊的 ANSI 字符集,由于它的字符集在不同的国家是不一样的,各国之间互不兼容,而且容量比较小,还是弃之不用比较好。
    
    
    
    
  • 相关阅读:
    AGC037F Counting of Subarrays
    AGC025F Addition and Andition
    CF506C Mr. Kitayuta vs. Bamboos
    AGC032D Rotation Sort
    ARC101F Robots and Exits
    AGC032E Modulo Pairing
    CF559E Gerald and Path
    CF685C Optimal Point
    聊聊Mysql索引和redis跳表
    什么是线程安全
  • 原文地址:https://www.cnblogs.com/golove/p/3222096.html
Copyright © 2011-2022 走看看