Coding中的编码问题之回顾&深入

[系列文章]上一篇:《Coding中的编码问题之系统学习》
[系列文章]下一篇:《这是最后一篇》

  到这里,相信你已经完整看完《Coding中的编码问题之入门&概览》《Coding中的编码问题之系统学习》,对字符编码已经有一个清晰的点到面的理解了,本文作为整个系列文章的完结篇,一来对之前所有内容进行一个回顾与总结,加深印象,扩宽理解;二来对没提及的剩下不多的几个细节进行补充说明,希望能在你的脑海里建立较为清晰的知识网络。非常希望你也能在阅读后有自己的理解,然后整理在你的笔记或博客中;也希望日后当某个知识点模糊不清时,还能帮到你、我。

字符编码笔记: ASCII,Unicode 和 UTF-8

  一开始,我们借 阮大神 的网络日志作一个回顾,阮大神 的介绍讲的非常通熟易懂,是学习的好文章,也是本人抒写博客的楷模,建议大家阅读原文。下面是阅读笔记,[红色部分] 为注解笔记。
  原文地址:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
  
 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位 (bit) 有 0 和 1 两种状态,因此八个二进制位就可以组合出 256 种状态,这被称为一个字节 (byte)。也就是说,一个字节一共可以用来表示 256 种不同的状态,每一个状态对应一个符号,就是 256 个符号,从 000000011111111
 非常通熟易懂,讲述了字节与编码的关系,也就是我们前面一直说的,字符到最终,都是 “0101…”,而如何从字符演变过来,就是上面那一套套字符编码体系。

 世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
 这便是乱码机制。

  Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。 有两个严重的问题,
  第一个问题是,如何才能区别 UnicodeASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?
  第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是 0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
 它们造成的结果是:
  1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode
  2)Unicode 在很长一段时间内无法推广,直到互联网的出现。
 这就是前面一直说的 Unicode 只是字符集,不是编码,编码是涉及具体的存储形式的;Unicode 有多种编码方式: UTF-8UTF-16UTF-32。这三种编码均在这上面两个问题中进行不同的折中,比如: UTF-8 就是唯一兼容 ASCII 的,不过,他存储汉字可需要三个字节,有点小浪费。

 UTF-8Unicode 的实现方式之一
 UTF-8编码规则很简单,只有二条:
  1)对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。故对于英语字母,UTF-8 编码和 ASCII 码是相同的。
  2)对于 n 字节的符号(n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode 码

 下表总结了编码规则,字母 x 表示可用编码的位。

1
2
3
4
5
6
7
   Unicode符号范围 | UTF-8编码方式
      (十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

 根据上表,解读 UTF-8 编码 非常简单。如果一个字节的第一位是 0,则这个字节单独就是一个字符;如果第一位是 1,则连续有多少个 1,就表示当前字符占用多少个字节
 回顾一下 UTF-8 的编码方案,再看一遍,还是觉得巧妙!

 这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头 (Big-Endian) 敲开还是从小头 (Little-Endian) 敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
 UCS-2 编码 方式,即直接用两个字节存入字符的 Unicode 码。第一个字节在前,就是 “大头方式”(Big endian),第二个字节在前就是 “小头方式”(Little endian)
 如果一个文本文件的头两个字节是 FE FF,就表示该文件采用大头方式;如果头两个字节是 FF FE,就表示该文件采用小头方式。Unicode 规范 中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做 “零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用 FEFF 表示。这正好是两个字节,而且 FF 比 FE 大1。
 UCS-2 好像是一种有别于 Unicode 的字符集;
 还有这个 BOM 被说得有点乱,不过这个这么有趣的故事,我必须贴在这里。大小头我们可以引申为高(大)低(小)字节,所以大端就是高字节在前,小端就是低字节在前;至于 BOM,其实是一个字符 “零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),不过类似 \0 是不可打印字符,但是字符就有对应的码点,这个字符对应的 Unicode 码点就是 U+FEFF,所以 “一个文本文件的头两个字节是 FE FF,就表示该文件采用大头方式”,反过来采用小端的话,低字节在前,就变成 FF FE 了。

 这篇网志中有两个地方需要补充一下,我忘了在原文中提到。
  1. 国际标准化组织通过了一套 ISO-8859-1 的编码,规定了单字节 256 个符号的编码方式。目前,这是 8 位编码的国际标准。
  2. Unicode 编码 中表示字节排列顺序的那个文件头,叫做 BOM(byte-order mark)FFFEFEFF 就是不同的 BOMUTF-8 文件的 BOM“EF BB BF”,但是 UTF-8 的字节顺序是不变的,因此这个文件头实际上不起作用。有一些编程语言是 ISO-8859-1 编码,所以如果用 UTF-8 针对这些语言编程序,就必须去掉 BOM,即保存成 “UTF-8—无 BOM” 的格式才可以,PHP 语言就是这样。
 这部分是我从该文章非常密集(很受欢迎的,这篇文章,热度可见一斑)的评论中找的。
 这个 ISO-8859-1 标准其实就是常说的,我们前面也提到的 Latin-1 编码;
 至于 UTF-8BOM,前面已经说过,人家没有大小端,统一大端,只有 带不带 BOM 的区别,而且常常有些时候是不要带 BOM 的,官方也推荐不带 BOM,为啥?兼容我们伟大的 ASCII 嘛。这就是为什么我们之前在 Qt 遇到乱码时,有个解决方案是因为 UTF-8 带了 BOM,所以阉割了,带 BOM 人家反而不认识了。可是偏偏 WindowsUTF-8 偏偏要带 BOM,哎… Linux 的默认编码好像就是 UTF-8—无 BOM,这就是差距,为什么,相信你可以合理的分析了。

 UTF-8 文件的 BOM “EF BB BF”,它实际上就是 FE FFUTF-8 编码 而得到的
 文中的 big endianlittle endian,翻译成“大尾”和“小尾”是不是更恰当?理由如下:
 FEFF: FF 比 FE 大且 FF 在后面,显示就是 “大尾”
 同样来自评论区。
 这个是上面的补充吧,我们之前也说了。至于 big endianlittle endian 你这样理解也行,不过,我现在倒觉得上面那种 “大小端~高低字节” 理解能记得更多知识点,也不乱。

 UTF-8 的表示算法: 既然第一个字节的 “1” 的数量就表示了整个当前字符的字节数,为什么后续字节还需要 “10” 作为前缀,这不是白白浪费了每个字节中的两个位吗
  也许是出于容错的考虑,网络传输或者兼容不够不严谨的编辑器,删掉汉字等字节字符中的部分字节,会使得整串字符乱码;
  当你处在一个字符串中间时,你不知道当前是一个什么字符,使用 UTF-8 能帮你在这种情况下同步到下一个合法字符,否则是不可能的。
 同样来自评论区。
 这个回答我觉得很准确,至于为什么能够区分,你不烦看一下 上一篇文章 “Unicode” 这一节讲述 什么是 UTF? 这一部分。还有,就是在设计时考虑到这一点,或者说我们怎样做到在设计某个方案时考虑到某些容错细节,很重要!

 Windows 从 NT 开始,一开始的内码是 UCS-2,只支持 Unicode BMP 字符,后来做了扩充,目前的内码就是 UTF-16,通常不严格地称为 Unicode
 同样来自评论区。
 立马打脸了,打得好!!!起码知道 UCS-2 是人家 UTF-16 的前身,那就是编码方式,不是字符集了,下面在《关于Unicode字符集 》 这篇文章里面我要去打作者的脸啦,等着…
 此外,也确实验证了我们前面了解到的,Windows 系统现在的内码是 UTF-16。不过,内码是什么?下面在 《字符编码详解——彻底理解掌握编码知识,“乱码”不复存在 》 这篇文章里会解释清楚。

 本文只是主要介绍了 UTF-8 编码,下面这篇文章对于 GB 码与 Big5 有更详细的介绍。
  汉字编码中现在主要用到的有三类,包括 GBKGB2312Big5
  1、GB2312 又称国标码,由国家标准总局发布,1981 年 5 月 1 日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共 7445 个图形字符,其中汉字占 6763 个。我们平时说 6768 个汉字,实际上里边有 5 个编码为空白,所以总共有 6763 个汉字。
   GB2312 规定 “对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为 “高字节”,第二个字节为 “低字节”。GB2312 中汉字的编码范围为,第一字节 0xB0-0xF7 (对应十进制为 176-247 ),第二个字节 0xA0-0xFE(对应十进制为 160-254 )
   GB2312 将代码表分为 94 个区,对应第一字节(0xa1-0xfe);每个区 94 个位(0xa1-0xfe),对应第二字节,两个字节的值分别为区号值和位号值加 32(20H),因此也称为区位码。01-09 区为符号、数字区,16-87 区为汉字区(0xb0-0xf7),10-15 区、88-94 区是有待进一步标准化的空白区。
  2、Big5 又称大五码,主要为香港与台湾使用,即是一个繁体字编码。每个汉字由两个字节构成,第一个字节的范围从 0X81-0XFE(即 129-255),共 126 种。第二个字节的范围不连续,分别为 0X40-0X7E(即 64-126),0XA1-0XFE(即 161-254),共 157 种
  3、GBKGB2312 的扩展,是向上兼容的,因此 GB2312 中的汉字的编码与 GBK 中汉字的相同。另外,GBK 中还包含繁体字的编码,它与 Big5 编码 之间的关系我还没有弄明白,好像是不一致的。GBK 中每个汉字仍然包含两个字节,第一个字节的范围是 0x81-0xFE(即 129-254),第二个字节的范围是 0x40-0xFE(即 64-254)GBK 中有码位 23940 个,包含汉字 21003 个。
 来自评论区。
 算是对 GB 系列编码的回顾吧。看来 GBK 还是 GB2312Big5 的合体呀,不过,大陆人发(制)明(订)的嘛,肯定不兼容台湾人用熟的 Big5…所以,GBK 兼容 GB2312,后者兼容 ASCII,故也兼容 ASCII
 此外,他们都是两个字节的,大概知道他们每个字节是有取值范围的,这样,像上一篇文章末尾的问题中,就可以用来判断非法字符的情况了。具体的取值范围,写代码的时候再查阅,太多要记的东西,不可能什么都记住呀。下面是对这篇网络日志的一个小结。

  • 这篇文章算是对 Unicode 那一块的全方位回顾吧,此外,了解了除了上面提到的三种,UCS-2 也是其一种编码方式,而且是 UTF-16 弱化版(因为 UCS-2 只是定长的两个字节)。
  • 加深对 GB 系列编码的了解。
  • 内码是什么?看来还有一些小概念没遇到,还好,下面会讲解一下。

关于 Unicode 字符集

  原文地址:http://blog.sina.com.cn/s/blog_4b4409c30100vw9t.html

 最初的 Unicode 编码 是固定长度的,16 位,也就是用两个字节代表一个字符,这样一共可以表示 65536 个字符。显然,这样要表示各种语言中所有的字符是远远不够的。Unicode4.0 规范考虑到了这种情况,定义了一组附加字符编码,附加字符编码采用 2 个 16 位来表示,这样最多可以定义 1048576 个附加字符,目前 Unicode4.0 只定义了 45960 个附加字符。
 Unicode 只是一个编码规范,目前实际实现的 Unicode 编码 主要有三种:UTF-8UCS-2UTF-16,三种 Unicode 字符集之间可以按照规范进行转换

  1. UTF-8
     UTF-8 是一种 8 位的 Unicode 字符集,编码长度是可变的,并且是 ASCII 字符集 的严格超集,也就是说 ASCII 中每个字符的编码在 UTF-8 中是完全一样的UTF-8 字符集 中,一个字符可能是 1 个字节,2 个字节,3 个字节或者 4 个字节长。一般来说,欧洲的字母字符长度为 1 到 2 个字节,而亚洲的大部分字符则是 3 个字节,附加字符为 4 个字节长。
     Unix 平台 中普遍支持 UTF-8 字符集,HTML 和大多数浏览器也支持 UTF-8,而 WindowJava 则支持 UCS-2
     UTF-8 的主要优点:
      对于欧洲字母字符需要较少的存储空间。
      容易从 ASCII 字符集UTF-8 迁移。

  2. UCS-2
     UCS-2 是固定长度为 16 位的 Unicode 字符集。每个字符都是 2 个字节,UCS-2 只支持 Unicode3.0,所以不支持附加字符。
     UCS-2 的优点:
      对于亚洲字符的存储空间需求比 UTF-8 少,因为每个字符都是 2 个字节。
      处理字符的速度比 UTF-8 更快,因为是固定长度编码的。
      对于 WindowsJava 的支持更好。

  3. UTF-16
     UTF-16 也是一种 16 位编码的字符集。实际上,UTF-16 就是 UCS-2 加上附加字符的支持,也就是符合 Unicode4.0 规范UCS-2。所以 UTF-16UCS-2 的严格超集
     UTF-16 中的字符,要么是 2 个字节,要么是 4 个字节表示的。UTF-16 主要在 Windows2000 以上版本使用。
     UTF-16 相对 UTF-8 的优点,和 UCS-2 是一致的。

小结:
1)、说 Unicode 是一种规范,这点确实,瞬间从字符集提升了一个 Level;所以说 Unicode3.0Unicode4.0 以及其中的区别和不同编码方式支持的规范不同,都是合理,让人信服的。

2)、不过作者字符集、编码一通说,我就不敢苟同了。你说 Unicode 字符集,很对;ASCII 字符集,也行,人家一种编码确实代表一个集合;但是说 UTF-8 字符集,那就大错特错了,你自己也说 UTF-8Unicode 的一种实现,人家还有其他实现呢!而这些实现都是对应 Unicode 这种字符集,字符集下又有各种字符集,这不太好吧。所以,我们要再次明确,字符集和编码的区别,Unicode 是一种字符集,他有很多种实现方式,UTF-8 编码方式就是其中之一。

3)、了解了 Unicode 字符集或者说 Unicode 规范制定和发展历程;通过对比各种 Unicode 编码方式的优点,再次回顾之前一直强调的 trade-off,也领会到 Unicode 在推动字符编码统一上采取的方案,值得借鉴和学习。

4)、最后要明确的,UCS-2UTF-16 的前身,是一种编码方式,现在基本都用 UTF-16 了,比如目前Window 内核和 Java 内存都采用 UTF-16 编码;
  此外,UTF-8 仍是目前国际化上最受欢迎的编码方式,Unix 平台(Linux 应该就是其一)和 网页 都支持或者说默认就是采用 UTF-8 编码,而且好像除了 wei ruan,没人觉得带 BOM 有多大意思。所以,为了跨平台也好,国际化也好,建议采用 UTF-8 编码;至于带不带 BOM,建议还是带吧,毕竟人家市场大,我们也用的好好的(我是渣渣地在 Win 上…,大神请忽略),另外我相信 Unix 平台 等这些好东西带不带 BOM 都能识别的!

字符编码详解

  最后,借 《字符编码详解——彻底理解掌握编码知识,“乱码”不复存在》 这篇文章作一个系统的总结,其中会引入几个新概念,有助于我们全面了解字符编码,也希望能进一步完善你脑海中的知识网络。原文总结的非常全面,可惜关于编码方式的诸多细节作者并没有过多提及;不过,在我们已经系统学习了字符编码之后,再进行阅读,是再好不过了。非常建议你能够有时间阅读一下原文,主要是文章末尾推荐的参考资料,至于其他内容,下面的笔记已经全面包括。
  还是老样子,[红色部分] 为注解笔记。

 每一个程序员都不可避免的遇到字符编码的问题,特别是做 Web 开发的程序员“乱码问题” 一直是让人头疼的问题,也许您已经很少遇到 “乱码” 问题,然而,对解决乱码的方法的内在原理,您是否明白?本人作为一个程序员,在字符编码方面同样遇到不少问题,而且一直对各种编码懵懵懂懂、不清不楚;在工作中也曾经遇到一个很烦人的编码问题。这两天在网上收集了大量编码方面的资料,对字符编码算是理解的比较清楚了。下面把我认为比较重要的知识点记录下来,一方面方便以后复习;另一方面也希望给跟我一样懵懵懂懂的人一个参考。不对或不妥之处,请批评指正。
 这个引入完美,想写好博客的,我们一起学习一下。

 1、字符集与字符编码
  字符是各种文字和符号的总称,包括各个国家文字、标点符号、图形符号、数字等。

  字符集是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有: ASCII 字符集ISO 8859 字符集GB2312 字符集BIG5 字符集GB18030 字符集Unicode 字符集 等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
  使用哪些字符,也就是说哪些汉字,字母和符号会被收入标准中。所包含 “字符” 的集合就叫做 “字符集”

  编码 (encoding) 和字符集不同。字符集只是字符的集合,不一定适合作网络传送、处理,有时须经编码后才能应用。如 Unicode 字符集可依不同需要以 UTF-8UTF-16UTF-32 等方式编码。
  字符编码就是以二进制的数字来对应字符集的字符
  因此,对字符进行编码,是信息交流的技术基础。
  规定每个 “字符” 分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做 “编码”

  各个国家和地区在制定编码标准的时候,“字符的集合” 和 “编码” 一般都是同时制定的。因此,平常我们所说的 “字符集”,比如:GB2312, GBK, JIS 等,除了有 “字符的集合” 这层含义外,同时也包含了 “编码” 的含义。

  注意: Unicode 字符集有多种编码方式,如 UTF-8UTF-16 等;ASCII 只有一种;大多数 MBCS(Multi-Byte Chactacter System,多字节字符系统)(包括 GB2312)也只有一种。
 看完之后是不是对字符集、字符编码的关系有更深刻的理解呢?作者这些概念的定义确实言简意赅,不知道是摘录的还是自己总结的,让我们很容易理解。
 看过上面的文字,终于明白,为什么那么多人把 字符集字符编码 两个概念给弄混了,原来这些是 Unicode 带来的颠覆性改变,科科。

 2、什么是内码?
  2.1 维基百科的解释
   在计算机科学及相关领域当中,内码 指的是 “将资讯编码后,透过某种方式储存在特定记忆装置时,装置内部的编码形式”。在不同的系统中,会有不同的 内码
   在以往的英文系统中,内码为 ASCII。在繁体中文系统中,目前常用的内码为 大五码(Big5)。在简体中文系统中,内码则为国标码(国家标准代码:现在强制要求使用 GB18030 标准;较旧计算机仍然使用GB2312)。而 统一码(Unicode) 则为另一常见内码。

  2.2 百度百科的解释
   内码是指整机系统中使用的二进制字符编码,是沟通输入、输出与系统平台之间的交换码,通过内码可以达到通用和高效率传输文本的目的。比如 MS Word 中所存储和调用的就是内码而非图形文字。英文 ASCII 字符 采用一个字节的内码表示,中文字符如国标字符集中,GB2312GB12345GB13000 皆用双字节内码,GB18030(27,533 汉字)双字节内码汉字为 20,902 个,其余 6,631 个汉字用四字节内码。
 反复看了很多遍,好像弄懂了点:
  (1)、内码指的仍是编码方式;
  (2)、之所以叫内码,特殊在他特指的是系统用的编码方式,有别于如:Java 在内存中使用 UTF-16 保存数据、某个网页使用的是 UTF-8 编码。
  (3)、英文系统内码为 ASCII、繁体中文系统常用的内码为 大五码(Big5)、简体中文系统内码则为国标码(我的 Windows 简体中文系统是 GBK,不知道文中后面那句话啥意思…),内码作为系统层面的编码,应该就决定了某些系统软件的默认编码(如我的 CMD 还有 NotePad 用的就是 ANSI 编码,下面会说 ANSI 编码)。

 3、字符编码分类总结
  3.1 ASCII 编码
   以下来自 “维基百科”:
    ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本 EASCII 则可以勉强显示其他西欧语言。它是现今最通用的单字节编码系统(但是有被 Unicode 追上的迹象),并等同于 国际标准 ISO/IEC 646
    ASCII 第一次以规范标准的型态发表是在 1967 年,最后一次更新则是在 1986 年,至今为止共定义了 128 个字符;其中 33 个字符无法显示(这是以现今操作系统为依归,但在 DOS 模式 下可显示出一些诸如笑脸、扑克牌花式等 8-bit 符号),且这 33 个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在 33 个字符之外的是 95 个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算 1 个可显示字符(显示为空白)。

   ASCII 缺点:
    ASCII 的最大缺点是只能显示 26 个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如: naïve、café、élite 等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而 EASCII 虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的苹果电脑已经抛弃 ASCII 而转用 Unicode

   最早的 英文 DOS 操作系统的系统内码是:ASCII。计算机这时候只支持英语,其他语言不能够在计算机存储和显示。
   在该阶段,字符串使用一个字节存放一个字符 (SBCS,Single Byte Character System)。如: “Bob123” 占 6 个字节。

  3.2 ANSI 编码
   为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如: 汉字 ‘中’ 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。

   不同的国家和地区制定了不同的标准,由此产生了 GB2312BIG5JIS 等各自的编码标准。这些 使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在 简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码

   中文 DOS、中文/日文 Windows 95/98 时代系统内码使用的是 ANSI 编码(本地化)。
   在使用 ANSI 编码 支持多语言阶段,每个字符使用一个字节或多个字节来表示 (MBCS,Multi-Byte Character System),因此,这种方式存放的字符也被称作多字节字符。比如,”中文 123” 在中文 Windows 95 内存中为 7 个字节,每个汉字占 2 个字节,每个英文和数字字符占 1 个字节。
   不同 ANSI 编码 之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码 的文本中。

   在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了 代码页(Code page)转换表 的技术来过渡性的部分解决这一问题,即通过指定的转换表将非 Unicode 的字符编码转换为同一字符对应的系统内部使用的 Unicode 编码。可以在 “语言与区域设置” 中选择一个代码页作为 非 Unicode 编码 所采用的默认编码方式,如 936 为简体中文 GBK950 为正体中文 Big5(皆指 PC 上使用的)。在这种情况下,一些非英语的欧洲语言编写的软件和文档很可能出现乱码。而将代码页设置为相应语言中文处理又会出现问题,这一情况无法避免。从根本上说,完全采用统一编码才是解决之道,但目前尚无法做到这一点。
   代码页技术现在广泛为各种平台所采用。UTF-7 的代码页是 65000,UTF-8 的代码页是 65001。
 ANSI 编码 其实是一个统称 (MBCS,Multi-Byte Character System),或者说一个多面手,在什么系统(简体中文、繁体中文…)就能指什么编码,反正这东西,微软出品,…
 我了去,果然,代码页(Code page)转换表 这种东西又是微软出品。不过,说句老实话,Windows 确实在支持汉字方面做得挺好的(之前在 Ubuntu 上要变成简体中文,折腾了很久…)。可能也是因为这么多麻烦的转来转去,没根本解决上面说的问题,才老是会出现乱码的情况。
 看样子,应该是和之前说的一样,现在的 Windows 内核 使用的编码应该就是 UTF-16 吧,估计就是内码了吧;不过 非 Unicode 编码 就用代码页(简体中文下就是 936(GBK)),这个也是内码吧;不过,大多数情况我觉得应该是 非 Unicode 编码 居多,也就是很多时候是 936(GBK) 或者说 ANSI 编码 (GBK)。好乱好乱,我们清楚一些不该说的,肯定错的就行,至于具体怎么理解,怎么说,我觉得本质的东西清楚就行。

  3.3 Unicode 编码
   为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。

   Unicode 字符集 可以简写为 UCS(Unicode Character Set)。早期的 Unicode 标准UCS-2UCS-4 的说法。UCS-2 用两个字节编码,UCS-4 用 4 个字节编码。

   在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集 中的序号。目前计算机一般使用 2 个字节(UTF-16)来存放一个序号 (DBCS,Double Byte Character System),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 “中文 123” 在 Windows 2000 下,内存中实际存放的是 5 个序号,一共 10 个字节。

   Unicode 字符集 包含了各种语言中使用到的所有 “字符”。用来给 UNICODE 字符集 编码的标准有很多种,比如:UTF-8UTF-7UTF-16UnicodeLittleUnicodeBig 等。
 挺喜欢这样的分类的,单字节、复合字节、双字节,总结的挺好的,对 ANSI 编码代码页 有新的认识,以后必要时加以区分就行。
 UCS 原来就是 Unicode 字符集 呀,那些 UCS-X 其实就是早期的 UTF-X,估计之前定长,现在有的变变长而已,改进了!
 关于 Windows 的内码,比如一个字符串的 sizeof 是多少,可能还真不好说,也许这就是 cl 编译器弄出个什么 执行字符集 的原因吧。不过我们要清楚,一个字符串在内存中究竟用几个字节保存,跟采用的编码方式是息息相关的,在目前已掌握的基础上,我们可以通过 sizeof 的大小推敲一下,也许就能解决一些乱码问题了。

 4、常用编码规则
  4.1 单字节字符编码
  (1)编码标准: ISO-8859-1
  (2)说明: 最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 ISO-8859-1 转化为字符串时,将直接得到 [U+00D6][U+00D0] 两个 UNICODE 字符,即 “ÖД
     反之,将 UNICODE 字符串通过 ISO-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。

  4.2 ANSI 编码
  (1)编码标准: GB2312BIG5Shift_JISISO-8859-2
  (2)把 UNICODE 字符串通过 ANSI 编码 转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。
     反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [U+4E2D] 一个字符,即 ‘中’ 字。
  “ANSI 编码” 的特点:
  (1)这些 “ANSI 编码标准” 都只能处理各自语言范围之内的 UNICODE 字符。
  (2)“UNICODE 字符”“转换出来的字节” 之间的关系是人为规定的。

  4.3 UNICODE 编码
  (1)编码标准: UTF-8UTF-16UnicodeBig
  (2)与 “ANSI 编码” 类似的,把 字符串通过 UNICODE 编码转化成 “字节串” 时,一个 UNICODE 字符可能转化成一个字节或多个字节
  与 “ANSI 编码” 不同的是:
  (1)这些 “UNICODE 编码” 能够处理所有的 UNICODE 字符。
  (2)“UNICODE 字符”“转换出来的字节” 之间是可以通过计算得到的。

  我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节,我们只需要知道 “编码” 的概念就是把 “字符” 转化成 “字节” 就可以了。对于 “UNICODE 编码”,由于它们是可以通过计算得到的,因此,在特殊的场合,我们可以去了解某一种 “UNICODE 编码” 是怎样的规则。
 最后一段话的思路,我觉得是正确的: “编码” 的概念就是把 “字符” 转化成 “字节”,至于怎么转,不同的编码采取的方式不一样。但其实过程都是相似的,首先收录字符,组成一个字符集;字符集里面的每个字符都对应于一个数字(在 Unicode 里面叫做码点);数字怎么转化为字节数据,遵循怎样的规则,这个就是编码。这就是每一套字符编码体系的共同之处。
 按照单字节、复合字节、Unicode 这样来划分编码方式也是可以的,反正我们根据上面 “编码” 的概念来进行理解。不过,对于 ANSI 编码,感觉还是不要和 Unicode 扯上关系,毕竟,ANSI 编码 都有各自的字符集,虽然这些字符集被收录在 Unicode 里面,但二者还是相对独立的。
 对 ISO-8859-1ISO-8859-2Shift_JIS 这些前面没提及的概念要有个了解,知道大概说的是什么,日后看到可以进行 推敲,更重要的是能够以此 构建搜索 (Google、Baidu) 的关键字

 5、编码的区别
  5.1 GB2312GBKGB18030
  (1)GB2312
   当中国人得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有 6000 多个常用汉字需要保存,于是想到把那些 ASCII 码 中 127 号之后的奇异符号们直接取消掉, 规定: 一个小于 127 的字符的意义与原来相同,但两个大于 127 的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从 0xA1 用到 0xF7,后面一个字节(低字节)从 0xA10xFE,这样我们就可以组合出大约 7000 多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的 “全角” 字符,而原来在 127 号以下的那些就叫 “半角” 字符了。这种汉字方案叫做 “GB2312”GB2312 是对 ASCII 的中文扩展。兼容 ASCII

  (2)GBK
   但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,不得不继续把 GB2312 没有用到的码位找出来用上。后来还是不够用,于是干脆 不再要求低字节一定是 127 号之后的内码,只要第一个字节是大于 127 就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 “GBK” 标准GBK 包括了 GB2312 的所有内容,同时又增加了近 20000 个新的汉字(包括繁体字)和符号。

  (3)GB18030
   后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。

  中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS”(Double Byte Charecter Set,双字节字符集)。在 DBCS 系列标准里,最大的特点是 两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于 127 的,那么就认为一个双字节字符集里的字符出现了。在这种情况下,”一个汉字算两个英文字符!”。然而,在 Unicode 环境下却并非总是如此。
 GB 系列的编码,进一步加深理解;另外,对于 全角/半角,有一定了解了吧。搜狗输入法 好像默认是通过 Shift + 空格键 进行 全角/半角 切换,有的话可以试试。
  5.2 UnicodeBigEndianUnicode
   这两个只是存储顺序不同,如 “A” 的 Unicode 编码6500,而 BigEndianUnicode 编码0065
 其实就是大小端啦,还有这里的 Unicode 编码 ,准确来说应该是 UTF-16 LE
  5.3 UTF-7UTF-8UTF-16
   在 Unicode 里,所有的字符被一视同仁。汉字不再使用 “两个扩展 ASCII”,而是使用 “1 个 Unicode”,注意,现在的汉字是 “一个字符” 了,于是,拆字、统计字数这些问题也就自然而然的解决了。

   但是,这个世界不是理想的,不可能在一夜之间所有的系统都使用 Unicode 来处理字符,所以 Unicode 在诞生之日,就必须考虑一个严峻的问题:和 ASCII 字符集 之间的不兼容问题。
    (1)、我们知道,ASCII 字符是单个字节的,比如 “A”ASCII65。而 Unicode 是双字节的,比如 “A”Unicode0065,这就造成了一个非常大的问题: 以前处理 ASCII 的那套机制不能被用来处理 Unicode
    (2)、另一个更加严重的问题是,C 语言使用 ‘\0’ 作为字符串结尾,而 Unicode 里恰恰有很多字符都有一个字节为 0,这样一来,C 语言的字符串函数将无法正常处理 Unicode,除非把世界上所有用 C 写的程序以及他们所用的函数库全部换掉。

   于是,比 Unicode 更伟大的东东诞生了,之所以说它更伟大是因为它让 Unicode 不再存在于纸上,而是真实的存在于我们大家的电脑中。那就是: UTF
   UTF= UCS Transformation Format,即 UCS 转换(传输)格式
   它是 Unicode 编码规则和计算机的实际编码对应起来的一个规则。现在流行的 UTF 有 2 种: UTF-8UTF-16,这两种都是 Unicode 的编码实现。
 Unicode 是字符集,UTF-X 是具体的编码方式(规则)。
 还有,GB 系列编码还可以这样理解: “两个扩展 ASCII”,不过 GB 系列很多汉字单个字节根本就不在 ASCII 里面吧,我还是相信这种编码是通过定义一套兼容 ASCII 的规则,然后镂空某些编号的,她还是对应一张大表(字符集),不是说人家有 94 个区嘛…
 “和 ASCII 字符集 之间的不兼容的两个问题” 确实很深刻,尤其是 “\0” 这一点,很多时候代码测试结果很奇怪,但看不出什么问题也许就跟这个有关,很少有人能想到这个点!

   5.3.1 UTF-8

1
2
3
4
  UCS-2编码(16进制)   UTF-8 字节流(二进制)
  0000 - 007F 0xxxxxxx
  0080 - 07FF 110xxxxx 10xxxxxx
  0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

    例如 “汉” 字的 Unicode 编码U+6C49U+6C490800-FFFF 之间,所以肯定要用 3 字节模板了: 1110xxxx 10xxxxxx 10xxxxxx。将 6C49 写成二进制是: 0110 110001 001001,用这个比特流依次代替模板中的 x,得到: 11100110 10110001 10001001,即 E6 B1 89

    可见 UTF-8 是变长的,Unicode 编码00000000-0000007F 的字符,用单个字节来表示; 00000080-000007FF 的字符用两个字节表示;00000800-0000FFFF 的字符用 3 字节表示。因为目前为止 Unicode-16 规范 没有指定 U+FFFF 以上的字符,所以 UTF-8 最多是使用 3 个字节来表示一个字符。但理论上来说,UTF-8 最多需要用 6 字节表示一个字符。

    UTF-8 兼容 ASCII
 这个 “理论上来说,UTF-8 最多需要用 6 字节表示一个字符”,估计是 Unicode 字符集能表示的最多字符对应的数字吧,之前有提到过,目前 Unicode 表示的所有字符(码点就到 U+10FFFF),UTF-8 用 1-4 字节即可表示。
   5.3.2 UTF-16(标准的 Unicode 成为 UTF-16)
    UTF-16 和上面提到的 Unicode 本身的编码规范是一致的。
    UTF-16 以 16 位为单元对 UCS 进行编码。对于小于 0x10000UCS 码,UTF-16 编码就等于 UCS 码对应的 16 位无符号整数;对于不小于 0x10000UCS 码,定义了一个算法。不过由于实际使用的 UCS2,或者 UCS4BMP 必然小于 0x10000,所以就目前而言,可以认为 UTF-16UCS-2 基本相同。但 UCS-2 只是一个编码方案,UTF-16 却要用于实际的传输,所以就不得不考虑字节序的问题
    UTF-16 不兼容 ASCII
 这个算法就是 使用代理区和代理对!另外,BMP 以内的字符能够使用 UCS-2 表示,和使用 UTF-16 是一样的,不过只有 UTF-16 能够表示 BMP 以外的字符。此外的区别,估计就是 “这个字节序的问题”UTF-16BOMUCS-2 应该没有带 BOM 之说。
   5.3.3 UTF-7
    UTF-7 (7-位元 Unicode 转换格式(Unicode Transformation Format,简写成 UTF)) 是一种可变长度字元编码方式,用以 Unicode 字元以 ASCII 编码的字元串来呈现,可以应用在电子邮件传输之类的应用
    UTF-7 并非Unicode标准之一。
 啊呀妈呀,UTF-7 不懂,这个估计没怎么会遇到,了解一下就行。

 6、Unicode 与 UTF
  Unicode 是内存编码表示方案(是规范),而 UTF 是如何保存和传输 Unicode 的方案(是实现)
  6.1 UTF 的字节序和 BOM
   6.1.1 字节序
    UTF-8 以字节为编码单元,没有字节序的问题。UTF-16 以两个字节为编码单元,在解释一个 UTF-16 文本前,首先要弄清楚每个编码单元的字节序。例如收到一个 “奎”Unicode 编码594E“乙”Unicode 编码4E59。如果我们收到 UTF-16 字节流 594E,那么这是 “奎” 还是 “乙”

    Unicode 规范 中推荐的标记字节顺序的方法是 BOMBOM 不是 “Bill Of Material”BOM 表,而是 Byte Order MarkBOM 是一个有点小聪明的想法:

     在 UCS 编码 中有一个叫做 “ZERO WIDTH NO-BREAK SPACE” 的字符,它的编码是 U+FEFF。而 U+FFFEUCS 中是不存在的字符,所以不应该出现在实际传输中。UCS 规范 建议我们在传输字节流前,先传输字符 “ZERO WIDTH NO-BREAK SPACE”

     这样如果接收者收到 FEFF,就表明这个字节流是 Big-Endian 的;如果收到 FFFE,就表明这个字节流是 Little-Endian 的。因此字符 “ZERO WIDTH NO-BREAK SPACE” 又被称作 BOM

    UTF-8 不需要 BOM 来表明字节顺序,但可以用 BOM 来表明编码方式。字符 “ZERO WIDTH NO-BREAK SPACE”UTF-8 编码是 EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8 编码了。
 读完这段,相信你对 BOM 的来龙去脉应该比较清楚了,好玩!机智!!
   6.1.2 BOM
   (1)BOM 的来历
    为了识别 Unicode 文件,Microsoft 建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACEU+FEFF)字符开头。这作为一个 “特征符”“字节顺序标记(byte-order mark,BOM)” 来识别文件中使用的编码和字节顺序。

   (2)不同的系统对 BOM 的支持
    因为 一些系统或程序不支持 BOM,因此带有 BOMUnicode 文件有时会带来一些问题
     ①JDK1.5 以及之前的 Reader 都不能处理带有 BOMUTF-8 编码的文件,解析这种格式的 xml 文件时,会抛出 异常:Content is not allowed in prolog
     ②Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。
     ③不同的编辑工具对 BOM的处理也各不相同。使用 Windows 自带的记事本将文件保存为 UTF-8 编码的时候,记事本会自动在文件开头插入 BOM(虽然 BOMUTF-8 来说并不是必须的)。而其它很多编辑器用不用 BOM 是可以选择的。UTF-8UTF-16 都是如此。
   (3)BOM 与 XML
    XML 解析读取 XML 文档时,W3C 定义了 3 条规则:
     ①如果文档中有 BOM,就定义了文件编码;
     ②如果文档中没有 BOM,就查看 XML 声明中的编码属性;
     ③如果上述两者都没有,就假定 XML 文档采用 UTF-8 编码。
 自己弱,还要叫别人背锅,真是 wei ruan!现在我们可以分析,UTF-8 带不带 BOM 根本没什么区别也没什么影响,做得好的东西都这么认为。估计 wei ruan 在自动识别 UTF-8 碰壁,或者这个不带 BOMUTF-8 让他们把之前补的坑都踩破了,无路可走了,厚着脸皮造了这个东西。(个人 YY…)
 为什么特别说 XML,个人认为本身 XML 就是用来传输数据的,用的编码方式肯定是最适合数据传输,兼容性最好的 UTF-8 (你可以看到很多 XML 文件开头都有这样的声明 <?xml version=”1.0” encoding=”UTF-8”?>),所以能不能很好解析带不或带 BOMXML 关系很密切!

  6.2 决定文本的字符集与编码
  软件通常有三种途径来决定文本的字符集和编码。
  (1)对于 Unicode 文本最标准的途径是检测文本最开头的几个字节。如:

1
2
3
4
5
6
 开头字节       Charset/encoding
 EF BB BF    UTF-8
 FE FF      UTF-16/UCS-2, little endian(UTF-16LE)
 FF FE      UTF-16/UCS-2, big endian(UTF-16BE)
 FF FE 00 00  UTF-32/UCS-4, little endian
 00 00 FE FF  UTF-32/UCS-4, big-endian

  (2)采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户。
   然而 MBCS 文本(ANSI)没有这些位于开头的字符集标记,现在很多软件保存文本为 Unicode 时,可以选择是否保存这些位于开头的字符集标记。因此,软件不应该依赖于这种途径。这时,软件可以采取一种比较安全的方式来决定字符集及其编码,那就是弹出一个对话框来请示用户。
  (3)采取自己 “猜” 的方法。
   如果软件不想麻烦用户,或者它不方便向用户请示,那它只能采取自己 “猜” 的方法,软件可以根据整个文本的特征来猜测它可能属于哪个 charset,这就很可能不准了。
 不过说真的,BOM 在识别编码上,还是挺有用的!

  6.3 记事本的几种编码
  (1)ANSI 编码
   记事本默认保存的编码格式是: ANSI,即本地操作系统默认的内码,简体中文一般为 GB2312。这个怎么验证呢?用记事本保存后,使用 EmEditor、EditPlus 和 UltraEdit 之类的文本编辑器打开。推荐使用 EmEditor,打开后,在又下角会显示编码: GB2312

  (2)Unicode 编码
   用记事本另存为时,编码选择 “Unicode”,用 EmEditor 打开该文件,发现编码格式是: UTF-16LE+BOM(有签名)。用十六进制方式查看,发现开头两字节为: FF FE。这就是 BOM

  (3)Unicode big endian
   用记事本另存为时,编码选择 “Unicode”,用 EmEditor 打开该文件,发现编码格式是: UTF-16BE+BOM(有签名)。用十六进制方式查看,发现开头两字节为: FE FF。这就是 BOM

  (4)UTF-8
   用记事本另存为时,编码选择 “UTF-8”,用 EmEditor 打开该文件,发现编码格式是: UTF-8(有签名)。用十六进制方式查看,发现开头三个字节为: EF BB BF。这就是 BOM
 建议还是使用 NotePad++,上面那些,有些用过,个人感觉并不好用,关于 NotePad++ 的使用可以看这里 《NotePad++》;此外,通过 记事本另存为 ANSI 编码,以十六进制方式查看文件,可以更清楚的认识 ANSI 编码。

 7、几种误解,以及乱码产生的原因和解决办法
  7.1 误解一
   在将 “字节串” 转化成 “UNICODE 字符串” 时,比如在 读取文本文件时,或者通过网络传输文本时,容易将 “字节串” 简单地作为单字节字符串,采用每 “一个字节” 就是 “一个字符” 的方法进行转化
   而实际上,在非英文的环境中,应该将 “字节串” 作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能 “多个字节” 才能得到 “一个字符”

   通常,一直在英文环境下做开发的程序员们,容易有这种误解。

  7.2 误解二
   DOSWindows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码 的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。 这使我们形成了一个惯性思维: “字符串的编码”
   当 UNICODE 被支持后,Java 中的 String 是以字符的 “序号” 来存储的,不是以 “某种编码的字节” 来存储的,因此已经不存在 “字符串的编码” 这个概念了。只有在 “字符串”“字节串” 转化时,或者,将一个 “字节串” 当成一个 ANSI 字符串时,才有编码的概念。

   不少的人都有这个误解。
 我的理解是,非 UNICODE 环境下,字符采取 ANSI 编码 保存;UNICODE 被支持后,采用 UTF-16 编码(如前面说的,JVM 中字符是按照 UTF-16 编码 保存的)。不过我知道,Windows 似乎是 UNICODE 环境,但是人家可以通过修改 执行字符集 来按照不同方式保存,乱呀!
 这个问题的结果就是,对于相同字符,采取不同编码方式,他在内存中实际存储的字节数据,甚至长度是不一样的,这就造成 sizeofstring.getBytes()string.getlength 这些代码在运行时的结果值得商榷。不过,注意一下就行,你都会那么多了,这个自然会分析。

  7.3 分析与解决
   第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。

   在这里,我们可以看到,其中所讲的 “误解一”,即采用每 “一个字节” 就是 “一个字符” 的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes(“iso-8859-1”) 来进行逆向操作,得到原始的 “字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, “GB2312”),来得到正确的 “UNICODE 字符串”
 整篇文章内容还是比较多的,而且总结的很全面;
 对 内码代码页半全角 等与字符编码相关的概念也能进一步了解;
 思路比较清晰,对我们建立相关的知识网络很有帮助。

最后的战役

 整个系列的文章就是这么多,从一开始在 Qt 中出现问题,分析问题产生很多疑问(来自 《借Qt中文乱码谈谈Coding中的编码问题》,可能你没看过,关系不大);到 《Coding中的编码问题之入门&概览》 一文对整个系列文章要阐述的内容—— 字符编码,作一个 OverView;然后就是 《Coding中的编码问题之系统学习》 一文中的系统学习,从点到面逐个击破;最后通过本文再总览性地回顾,加深印象,同时对最后几个知识点进行了解。希望看完本系列文章,能帮助你在脑海里形成字符编码的知识网络:

  1. 字符集、字符编码的联系,包括先有字符集,确定每个字符对应的编号,最后采用具体的字符编码存储这些编号,形成从符号到 “0101…”的映射。

  2. 如何存储字符集中每个字符的编号其实是一个具体的问题。这个问题的解决可以有两条思路,定长或变长。其中还要考虑如何解决一个随时时间推移而产生的兼容性问题。

  • 以上两点都是理论性指导,应用在具体实践上,就产生了 由简单到复杂由容量小到容量大由一到多再到一 的字符编码发展史。
  1. 鼻祖 ASCII 奠定了字符编码的基调;随着欧洲国家的加入,Latin-1 自然需要对 ASCII 进行扩展;紧接着,以中国为首的来自世界各地的人们的加入,扩展显得迅捷但七零八落,GB 系列等编码不断出现和发展;直到目前,随着国际化的发展,统一的字符编码标准显得非常迫切,Unicode 来了!不过,统一的道路总是那么漫长,一方面要兼并(兼容 ASCII),一方面还要劝降(使用 UTF-16),所以 一国两制 就出现了,在不同领域,UTF-8UTF-16 各领风骚。
  2. GB 系列编码的发展是一部曲折史,GB2312 的横空出世,确定了 GB 系列编码兼容 ASCII 的优良传统;GBK 接过旗帜,延续优良传统,扩展字符容量;可惜,到最后,GB18030 因小失大,容量是大了很多,可惜兼容性上捉襟见肘,目前 GB 系列编码只能吃 GB2312/GBK 的老本了。
  3. Unicode 一统大业也是历经波折。一开始的定长策略,UCS-X 方案纸上谈兵;接下来的变长方案打下江山。UTF-16 凭借 “代理区代理对” 的策略,给别人以震慑;UTF-8 借灵活、巧妙之法所向披靡,对 ASCII 的无缝衔接更是其最大威力。目前,UTF-16UTF-8 同时在不同领域发挥着巨大的作用。统一大业,指日可待。
  • 除了这样一部发展史,还要熟记几个概念:
     (1)、跟 Unicode 码点相关的 BMPSP
     (2)、跟 UTF-16 编码相关的 代理区(Surrogate Area)代理对(Surrogate Pair)
     (3)、解决 wei ruan Unicode 环境与 ANSI 编码鸿沟的 代码页(Code Page)
     (4)、UTF-8 的死对头 BOM
     (5)、跟 GB 系列编码密切相关的 区位码国际码机内码
     (6)、特指系统使用的编码方式—— 内码
  • 熟记几个算法:
     (1)、UTF-8 1-4 字节编码方案;
     (2)、UTF-16 2 或 4 字节编码方案,主要是 BMP 之外字符的编码方案;
     (3)、GB 系列编码兼容 ASCII 的策略,注意 GB2312GBK 的异同点。
文章目录
  1. 1. 字符编码笔记: ASCII,Unicode 和 UTF-8
  2. 2. 关于 Unicode 字符集
  3. 3. 字符编码详解
  4. 4. 最后的战役