今天早些时候,有小伙伴反映说有个病人的名字中包含了“䞍”字,但是这个字存到数据库却变成了“?”。遂决定解读一下字符编码。
我们经常戏称计算机拥有二进制的脑袋,因为计算机采用二进制进行运算,而二进制有且只有0和1两个数字组成。我们前面提到的“䞍”字也不例外,最终也是要转换成0和1组成的二进制存到数据库中。
提到计算机,我们不得不提一个从来不把别的国家看在眼里的国家,这个国家就是美利坚合众国,世界上第一台计算机就诞生于此,1946年2月14日,在美国宾夕法尼亚大学诞生了世界上第一台计算机。
为了运算简单,计算机采用二进制运算,但是美国人是用英语交流的,为了让计算机能够识别自然语言,他们尝试将字符组成的自然语言跟二进制做一个对照。因为英文字母比较有限,所以一个字节足以能够代表所有美国人使用的字母加数字加特殊符号。这样American Standard Code for Information Interchange(ASCII)就诞生了。自此以后,美国人民能够尽情的使用计算机了。
美国使用计算机的先进技术,终于被其他国家知道了,大家都想使用美国的先进技术,而美国人民从来不会放过炫耀自己技术的机会,所以他们慷慨的把计算机技术炫耀给了其他国家。就拿中国来说吧,计算机拿来之后,我们突然发现,这个东西并不适合中国的国情,古老的中华文明给我们留下了宝贵的汉字文化,中国人民还是期望计算机能接受汉字信息,但是中国常用的汉字7千多个,汉字总共要9万多个,而一个字节只能组合成127种不同的数值,也就是说最多能表示127个字符,根本不能用啊。于是乎,中国人民毫不客气的跟美国人说,你们发明的这个东西,不适合我们,还是你们自己玩吧。
美国人听到这话不高兴了,我们这么先进的技术怎么可能不适合你们呢?听了中国人民的诉苦后,美国人民微微一笑,说,这个简单,一个字节,无法跟汉字做对照,我们可以使用两个字节啊。于是乎,AMERICAN NATIONAL STANDARDS INSTITUTE定义了一种新的字符编码(ANSI)作为ASCII码的拓展,ANSI编码用0x00~0x7f (即十进制下的0到127)范围的1 个字节来表示ASCII码,超出一个字节的 0x80~0xFFFF 范围来表示其他语言的其他字符。这样算下来,两个字节最多可以存储的字符数目是2的16次方,即65536个字符,虽然无法表示所有的汉字,但是足以表示所有的常用汉字了。
美国人喜欢定标准,定完标准,高呼一声,你们按照这个标准去实现自己的编码对照吧。所以ANSI标准一出,地球上的国家纷纷编写自己语言的对照。中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订了《汉字内码扩展规范》,采用“国标扩”三个单词的首拼命名为“GBK”。其编码范围从8140至FEFE(剔除XX7F),共23940个码位,共收录了21003个汉字。很不幸,前文提到的“䞍”字并没有被GBK收录。
我们知道windows中文操作系统的默认编码就是GBK,而我们常用的pb9.0开发工具就是基于ANSI编码的,运行在中文操作系统下面也就是使用了GBK编码,所以“䞍”字也无法在PB中显示。同样的为了适应PB开发工具,ORACLE数据库,服务端的字符集,我们选用了ZHS16GBK。很多人不知道ZHS代表什么意思,我从oracle官网查询到,ZHS代表了Simplified Chinese。而16代表了16位二进制也就是两个字节,GBK代表了GBK编码,也就是说ORACLE数据库如果服务端选择了ZHS16GBK字符集的话,同样无法保存前面提到的“䞍”字。
有的人可能会说,varchar2类型的字段无法保存“䞍”字,但是网上说nvarchar2类型的字段却能保存,这是因为nvarchar2默认采用的字符集是AL16UTF16。UTF编码我们后面会提到。
采用ANSI之后,韩国人民又要把计算机给退回美国去了,我们知道韩国人民喜欢研究中国文化,当他们把韩文和中文同时录入文档里面时,这个破编码又不认识中文了。
聪明的美国人,其实早在1990年就意识到,随着国际间的交流,每个国家自己编写自己的ANSI在自己国家使用是可以的,但是,国家跟国家之间的语言是无法同时出现在同一个文档中的,因为他们相互不承认对方的编码对照。为了防止自己的技术被质疑,他们决定解决国家之间编码冲突的问题,终于他们绞尽脑汁想出了可以容纳世界上所有文字和符号的字符编码方案,那就是Unicode码。Unicode码由国际组织于1990年开始研发,1994年正式公布。庆幸的是,Unicode码里面收录了“䞍”字,这也就是为什么txt文本里面选择Unicode码就能保存“䞍”字的原因。
既然,Unicode码一统天下了,是不是编码技术就终止了呢?美国有个计算机天才叫Ken Thompson,他发现了一个问题,Unicode码统一采用两个字节进行编码,但是所有英文字符,使用一个字节就足够了啊,这样的话,存储空间就浪费了一倍,方便其他国家固然是好的,但是不能损害美国的利益。于是乎,1992年他研究出了另一种编码,Unicode的可变长度字符编码,又称万国码,那就是UTF-8。而前面我们提到过UTF-16,这里我们就说一下UTF-8,UTF-16和UTF-32的区别。UFT-8:一种变长的编码方案,使用 1~6 个字节来存储。UFT-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储。UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。
至此,我们把编码的来龙去脉通过通俗易懂的语言理了一遍,也拿美国人,中国人和韩国人开了一下玩笑,纯粹是为了方便读者理解,本人声明不对任何国家的人持有不好的感情色彩。