一、前言
一直以来,都对字符编码有一个较模糊的概念,更谈不上懂得其中的原理。本文记录自己对字符编码的学习。
二、概念
字符编码(英语:Character encoding)也称字集码,是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。
计算机是不认识字符(用来表明达意文字或者符号)的,也不能直接存储字符。所以计算机工程师们就用一些规则,按照这个规则,把一个个字符编成计算机能够处理和存储的二进制。字符全部使用某一规则编码,就有了这些字符的集合,即字符集。例如常用的ASCII、GBK、Unicode等字符集。
三、计算机中的二进制
计算机如何将二进制转换为人可以读的懂文字呢?二进制又如何存储在计算机里的呢?
我们都知道计算机有个CPU,CPU内部是通过大量的逻辑门电路实现计算的。逻辑门是在集成电路上的基本组件。简单的逻辑门可由晶体管组成。这些晶体管的组合可以使代表两种信号的高低电平在通过它们之后产生高电平或者低电平的信号。高、低电平可以分别代表逻辑上的“真”与“假”或二进制当中的1和0,从而实现逻辑运算。逻辑门的实现直接应用了二进制,因此现代的计算机和依赖计算机的设备里都用到二进制。每个数字称为一个比特(二进制位)。
四、字符集历史
历史上最先发明的编码规则是ASCII(American Standard Code for Information Interchange,美国信息交换标准代码),ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符。他这128个字符主要用于显示英语。也就是说其它字体用ASCII显示不了,例如汉字、朝鲜字等等。
随着计算机的发展,应用到各国,ASCII字符集明显是不够用,我想用计算机显示汉字怎么办呢?于是在1981年,中国人自己发布了GB 2312 或 GB 2312–80,后来由于还是不够用,于是厂商微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码。这个···于2000年3月17日推出了GB 18030-2000标准,以取代GBK。GB 18030-2000除保留全部GBK编码汉字,在第二字节把能使用范围再度进行扩展,增加了大约一百个汉字及四位元组编码空间,但是将GBK作为子集全部保留。请参看GB 18030。
历史就不扯了,扯不完。看一幅图片
当计算机传到世界各个国家时,为了适合当地语言和字符,设计和实现类似GB232/GBK/GB18030/BIG5的编码方案。这样各搞一套,在本地使用没有问题,一旦出现在网络中,由于不兼容,互相访问就出现了乱码现象。为了解决这个问题,一个伟大的创想产生了——Unicode。
五、二进制与字符集
以ASCII字符集为例,探讨一下二进制与编码的关系。
从ASCII可显示字符图,我们可以看到二进制与图形(字符)是一一对应的关系,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。我们再看看Unicode码。
六、Unicode码
Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。
Unicode伴随着通用字符集的标准而发展,同时也以书本的形式对外发表。Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2016年6月21日公布的9.0.0,已经收入超过十万个字符(第十万个字符在2005年获采纳)。Unicode涵盖的数据除了视觉上的字形、编码方法、标准的字符编码外,还包含了字符特性,如大小写字母。
Unicode发展由非营利机构统一码联盟负责,该机构致力于让Unicode方案替换既有的字符编码方案。因为既有的方案往往空间非常有限,亦不适用于多语环境。
Unicode备受认可,并广泛地应用于电脑软件的国际化与本地化过程。有很多新科技,如可扩展置标语言(Extensible Markup Language,简称:XML)、Java编程语言以及现代的操作系统,都采用Unicode编码。
前面说到ASCII码是用一个字节表示,不够用,来个Unicode码用4个字节表示。比如,汉字"超"的unicode是十六进制数8D85,转换成二进制数足足有15位(1000 1101 1000 0101),也就是说这个符号的表示至少需要2个字节。对于汉字,或许用2个字节就够了,但是表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这就出现了问题,想想如果字符都用4个字节表示,例如“超”用4个字节表示就是(0000 0000 0000 0000 1000 1101 1000 0101),前面的十六位都是0,这不就对计算机存储和运算带来极大的浪费以及负担。所以这也出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,例如,2个字节存储,3个字节存储,甚至是4个字节存储,都可以用来表示Unicode。也造成了Unicode在很长一段时间内无法推广,直到互联网的出现。
七、UTF-8
互联网的普及,强烈要求出现一种统一的编码方式。目前的Unicode字符分为17组编排,0x0000 至 0x1FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。
UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
Unicode编码(十六进制) | UTF-8 字节流(二进制) |
---|---|
000000-00007F | 0xxxxxxx |
000080-0007FF | 110xxxxx 10xxxxxx |
000800-00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
010000-10FFFF | 11110xxx10xxxxxx10xxxxxx10xxxxxx |
UTF-8的特点是对不同字节范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是6个字节。从上表可以看出,6字节模板有31个x,即可以容纳31位二进制数字。Unicode的最大码位0x7FFFFFFF也只有31位。满足了不同长度字符的需要,也不会造成极大的浪费,也能识别编码处于什么范围。
跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
例2:Unicode编码0x20C30在0x010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。
八、总结
通过以上解读,对字符编码又有了一个更深的印象。本文只是一个对字符编码进行一个浅显的介绍。详情还需看参考资料。另外与编码原理相关的很多很有意思地方,例如,乱码,摩斯密码,二进制在计算机中的处理思维等等,听说还有三进制计算机。
文章中存在不准确之处,希望大家批评指正。
参考资料:
1、https://zh.wikipedia.org/zh/UTF-8
2、http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
3、https://zh.wikipedia.org/wiki/Unicode
4、https://zh.wikipedia.org/zh/ASCII