字符集与字符编码 (charset & encoding)

乱码是个大坑,相信每一个人都遇过,并且是个绕不过去的坑。我理解每一个程序员都应该写一篇编码相关的博文,梳理本身对这一块的理解,下面是我反复理解屡次以后的学习小结。html

 

一、从记事本的不一样编码提及:程序员

打开记事本,输入“我我”,保存为ansi编码(实际上是gb2312,这也是默认编码)。再分别另存为unicode(实际上是utf-16 little endian)、unicodeBigEndian(实际上是utf-16 big endian)、utf8,用UltraEdit打开,切换到二进制模式,内容以下:windows

编码 内容
ansi CE D2 | CE D2
unicode FF FE | 11 62 | 11 62
unicode big endian FE FF | 62 11 | 62 11
utf-8 EF BB BF | E6 88 91 | E6 88 91

能够看出,“我”在gb2312里的编码是CE D2,在utf-16 little endian里是11 62,在utf-8里是E6 88 91。FF FE是文件头,用来标识这个文件是unicode little endian格式的,同理,EF BB BF标识文件是utf-8格式。所谓的endian,是字节顺序,不一样的操做系统,可能小字节在前、也可能大字节在前,既然不同,就用一个文件头(学名叫BOM,byte order mark,字节顺序标识符)来标识。具体方法是:找一个在unicode里不存在的字符(FF FE)来表示,EF BB BF是FF FE在utf-8里的编码。各类编码的BOM详见下表:数组

bytes encoding
EF BB BF utf-8
FF FE utf-16 little endian
FE FF utf-16 big endian
FF FE 00 00 utf-32 little endian
00 00 FE FF utf-32 big endian

 

二、乱码的各类现象工具

文本保存在文件里都是字节byte,那这些byte数组是怎么显示为具体的字符呢?以ansi格式的“我”为例,CE D2显示在界面上的步骤是这样的:windows首先将CE D2转换成它内部使用的编码格式unicode,而后按照unicode编码去字体文件中查找字体图像,最后将图像画在窗口的指定位置上。以下:学习

  1. 字符首先以某种编码保存在文件中。
  2. windows将文件中的编码映射为unicode编码。
  3. windows根据unicode编码去字体文件中查找字体图像,并画在窗口上。

这3步中的每一步错了,都体现为一种典型的乱码。字体

  1. 错误1:若是弄错了编码格式,好比将“我”的utf-16编码11 62错存成了gb2312的文件,就会出现乱码。
  2. 错误2:若是映射到unicode出错,例如出现了unicode里未定义的字符编码,windows就会使用缺省字符,一般是?。好比“我”的gb2312编码CE D2在unicode里还没有使用,因此映射不到任何一个有效的unicode编码。
  3. 错误3:若是映射到了unicode编码,但在字体文件中找不到对应的字符,windows就会显示字体文件中的缺省图像:空白或方格。

 

三、各类编码ascii、ansi(gb2312/big5/...)、unicode(utf8/16/32/...)编码

1) ascii用1个字节(共255个)表示全部英文字符。缺点很明显,就是不够用。对于欧洲那些表音的字母类,就已经捉襟见肘了,再遇到中日韩的表义字符动辄上万,就更不够了。因而ansi想出来填坑,但结果是反而把事情搞麻烦了。其实若是一开始就想到字符不够用的问题,再直接整出个全球统一字符集unicode,就不会有这个大坑了。因此说白了,乱码问题是个历史缘由形成的问题(话说软件里那么大坑,哪些不是历史缘由形成的!Y2K也是一例,可是谁也没那个远见,因此只能迂回前进了)。url

 

2) 而后,各国都发现ascii不够用,因而各自造出本身的编码来知足须要,中国造出gb23十二、台湾造出big5大五码、日本造出shift_jis、其余阿拉伯国家、印度也是相似的。先是IBM弄了代码页(CodePage)、再是微软继承了这一套再加了些本身的定义,整成了ansi,包括全部地区的代码页。经过这些代码页,windows能够实现各地区编码与unicode之间的相互转换。(注:代码页的具体文件为:c:\windows\system32\*.nls文件)代码页是为了兼容已有的程序而存在的,若是咱们能强制全部程序都转到unicode,那各地区的ansi编码也就没有存在的必要了,说白了只是发展过程当中的中间产物。spa

 

3) 最后是unicode。

3.1) 先说说字符集与字体编码的区别。对大多数字符集来讲,字符集里的编码 = 字符编码。好比“我”字,在gb2312中的编码是CE D2,在big5中是A7 DA。惟独在Unicode里例外,Unicode使用4个字节/32位(实际只用了31位,最高位必须为0)惟一表示全世界全部可能的字符。unicode的容量为231-1 ≈ 21亿个,截止到2005年只用了10万个,因此应该足够用了。但每一个字符4字节,太浪费空间了,因而就产生了各类编码方式,常见的有utf-八、utf-1六、utf-32等。这里,unicode是字符集,utf-8/16/32是字符编码。一样是“我”字,在unicode里是00 00 62 11,在utf-8里是E6 88 91,在utf-16里是62 11,在utf-32里才是00 00 62 11。

3.2) unicode到utf8/16/32的转换方式就不详述了。无非又是一些位操做,对理解这个问题的主干无益。有兴趣的能够参看本文最后的引用。

 

四、对比一下各编码的存储效率

  1. 存储英文时:utf-8 = ansi > utf-16 > utf-32
  2. 存储中文时:ansi ≈ utf-16 > utf-8 > utf-32

固然,这里说的是大多数的状况,中英文混杂、多语言混杂、小语种文本都须要具体讨论。

 

五、参考

  1. 伐木丁丁鸟鸣嘤嘤:《浅谈文字编码和Unicode(上)》和他作的小工具CodeView:快速查看文本编码
  2. 阮一峰:《字符编码笔记:ASCII,Unicode和UTF-8
  3. 中韩翻译网 金圣镇:《字体编辑用中日韩汉字Unicode编码表
相关文章
相关标签/搜索