字符编码

来源:互联网 发布:中英文转换器软件 编辑:程序博客网 时间:2024/06/11 17:11

计算机的字符编码是所有人每天都用得到,见得到,但是似乎在基础计算机课程教材很少被关注的内容(例如佛罗赞的《计算机科学导论》对各种编码也只提了一页不到的内容)。而互联网上容易查到的往往是某个特定编码如何实现的技术细节,而我们平时会接触到的GB,ASCII,Unicode,UTF-8,UTF-16等编码分别代指了什么意思?为什么会发展成今天这样,都是很少被提及的,但一旦涉及底层总会接触到一些编码的知识,要是完全不明白编码的差异似乎也不太好。本文不会谈技术实现细节(因为字符编码也不是我的研究方向)只简要的谈一谈为什么通用的字符编码会是我们今天看到的这个样子

在文章之前要谈一谈字符代码和字符编码的差异。计算机可以表示的字符组成了一个集合,这个集合我们称为字符集,和数学中定义集合是无序的不同,此处集合中的每一个字符都拥有一个唯一且不和其它字符重复的序号,作为在这个字符集里的位置的标识。例如今天通用的Unicode标准,用的字符集称之为UCS-4(4-bytes Universal Character Set,4比特通用字符集),里面的每一个字符的标识都是4比特32位的二进制数,这个数我们称之为字符代码。字符编码则是指字符在传输过程当中用于表示字符的二进制流。按照直觉这两个东西理应一致,例如ASCII的代码和编码就是一样的,但是其实也可以不一样,这就引出一个问题,为什么要定义和字符代码不一样的字符编码?这就是UTF和Unicode主要要讨论的问题。


标准信息交换

计算机字符编码,最初使用的是ASCII码(America Standard Code for Information Interchange,美国标准信息交换码),由于ASCII的字符代码和编码一致,我就不在术语上区分了。每个字符用一个字节表示。8位用了7位,保存128种字符对应的二进制代码,最后一位作奇偶校验,ASCII码表网上可以很轻易的查到。之所以要有这个标准,是因为计算机只能处理0和1,在传输和保存字符时,传输和保存的实际上是比特流而非字符本身。计算机需要一套二进制代码和字符之间的一一映射关系,使得计算机能够在读取到特定的比特流时,能够把这个比特流识别成一个个的字符,在存储字符时,也能够按照这个规则把每个字符保存成特定的二进制代码,从而完成信息的识别。如果这个映射是不唯一的,那么在不同设计的计算机上进行信息交互时,就会出问题(计算机A能读到计算机B传过来的正确的比特流,但是A无法还原比特流包含的信息)。因此信息交互时要有一个统一的标准,保证各方的信息在相互交换之后可以被识别,美国国家标准协会(American National Standard Institute,ANSI)据此设定ASCII码,规定了所有字符交互时的二进制代码和字符之间的对应关系,因此ASCII叫做标准信息交换码。

由于ASCII码的设计只考虑了英语,故只用了8位作为单元最多表示256种字符,因此在全球推广时遇到了很大的尴尬,ASCII的先天缺陷使其无法克服字符不够用这种障碍。在计算机推广到全世界的初期,全世界都遇到了ASCII编码无法映射本民族语言文字的问题。因此当时全世界各个地区都在根据自己的需求拓展字符编码标准。由于传输和保存字符本质上的全部都是比特流,美国国家标准协会制定ASCII,建立了英语和二进制码的对应关系,建立信息的交互标准,那其它国家自然也可以自己设定规则建立本国语言和二进制代码之间的对应关系,从而完成本国语言在计算机上的保存和编码。我们国家自1980年就有自己的GB2312标准(字符编码和代码依旧一致),用两个字节表示一个汉字,自己建立了一套规则并不断进行更新,直至今天Windows 10记事本的默认保存编码,ANSI,使用的就是我国的自定的国家标准,当然为什么我国的国家标准在Windows 10显示的名字是美国标准这是一个很迷的事情,似乎多数人认为这是微软瞎取名字的习惯的体现。就当前,2016年Unicode已经广泛使用的今天,ANSI旨在解决这样一种情况:Windows中或许要碰到那些几十年前写的老文档,那么系统保留以往的以往的编码方式从而能够正确打开老文档是必要的,这种编码根据各个国家和地区历史上自己定义的字符集,地区之间不统一,大陆的简体中文系统就是国家标准总局发布的国家标准(GB),台湾则是Big 5。而因为各个国家各行其是,哪怕是台湾和大陆这样使用同一语言的地区,由于政府不同,也建立了两套不一样的编码标准。那么台湾的数据文档在大陆打开,由于大陆在售的计算机大概不会预装台湾的编码标准,那么由于编码规则不一样,大陆就无法读取台湾文档中的字符。类似的问题在全球广泛出现,使得信息在跨国跨语言时的交互非常麻烦。在出版业则更加麻烦,出版业终归会有在一个文档里需要处理很多种语言的需求,各个语言的编码标准完全不同这就让同时要处理多钟语言的出版业很难办。因此从上世纪八十年代初开始,多语言编码方案就开始进入行业(可怕,8051的年代啊),例如IBM和施乐公司(毕竟是打印机鼻祖)都有多语言编码的产品。随之而来的趋势就很显然了,各个国家都要用计算机,各个国家独立搞出的字符编码标准会在通用性上有很大的麻烦,那么,应该建立一套全球统一的编码系统,包含所有国家的文字字符从而实现标准信息交换,这样全球传输都不会有任何字符无法识别的问题。


16位与32位的纷争

一个很显而易见的问题是,字符编码要拓展,按照计算机的习惯,应该是拓展到一个字符两个字节,还是四个字节?这个差异是非常大的,4个字节和2个字节反应在文档上是差整整一倍的大小,当年不像现在,存储对大部分人来说便宜的跟不要钱一样,网速又很快,网络中的主导流量是多媒体。当年不论计算机本身还是存储空间都很昂贵,因此16位和32位所造成的文档差一倍大小,涉及到了极大的经济利益,能用16肯定用16。

一开始的通用多语言字符编码是有两套平行的标准的。第一套是美国,主要是施乐公司主导的,名字叫做Unicode(施乐的帕洛阿尔托研究中心真的是Bug,Unicode一开始也是他们倒腾出来的),建立的字符集称之为UCS-2(2比特通用字符集,因为字符代码只有16位,所以最多表示65536个字符),Wikipedia上Unicode词条的参考资料中可以看到Unicode的第一个版本,Unicode 88的综述,我只挑最重要的谈。Unicode最初的设计文档除了阐释通用字符编码工程的重要性之外,谈的最关键的问题就是16位够不够用,因为哪怕仅仅是在八十年代,对于旨在提供全球统一的编码的Unicode,最多65536个字符似乎也是少了一点。Unicode 88当然解释了为什么65536够用,理由在于两点。其一,Unicode 88的制定者说,我们制定的标准是面向未来的,只要加入现在还在用的字符就好,那些正在消亡的语言我们觉得就不要放在标准里占空间了,判定字符是“Modern Use”还是“Obsolete”的依据是是否还出现在当代的出版物中。他们(自称)统计了1988年全世界所有发行的报纸和杂志,认为一整年都没有出现在这些出版物的字符是正在消亡的,没有必要加入标准。其二,对于世界上字符最多的汉字,针对中日韩三国的汉字很多都是类似的特点,因此可以把中日韩类似的汉字统一表示(也就是所谓的“中日韩统一字符”,我查了一下,这是一段中日韩互相撕逼和对美国撕逼的血雨腥风,但是和主题的关联度不是特别大就不写了),避免了三国语言的重复输入。这样两个标准设定后,符合标准要求的,全球语言总字符数少于2的14次方个,16位绰绰有余,因此Unicode 88标准非常慷慨,有一半的空间是留给用户自定义和将来标准补充的,16位只给已经定义的字符分配了不到一半的空间,显得特别空旷。这种标准提出后就经美国强力推广,微软用了这个标准写了Windows NT的内核,苹果就更不用说了,最著名的Unicode运动领导公司。美国人的标准看似很有道理,事实上拉丁语系国家也不会觉得有什么问题,但中国人,日本人和韩国人是无法接受的。一位叫做Steven J. Searle的日本工程师针对这一问题写过两篇文章,我会附在后面,直斥美国的标准根本不配叫国际标准,而是傲慢的美国为自己产业量身定做的。哪怕不考虑汉字只能留下一部分对中国不利,这样一种民族主义,我也必须承认这个日本人的质疑十分尖锐而合理。美国人的吃相太难看了,在Unicode标准的综述中,一边说Unicode要成为通用标准编码,一边16位本来就是没办法容纳所有字符的,提的那两个字符筛选标准看上去还像那么回事但一深究就站不住脚,对语言的重要性进行排名,进行筛选,而排名的标准居然是国家的GNP,赤裸裸的看谁钱多就给谁面子,92年Unicode的1.1版只有24种语言,就是按照经济实力排序的,直接无视了小国家。要不是因为中日韩三国GNP占全球14%(88年)排第二,又占着地图上根本没办法忽略的土地,美国人会不会给面子分给三国那么大一块空间很值得怀疑。另一个质疑则是文化霸权:凭什么有用没用是你来界定的?我中日韩三国古汉语不用的那么多,难道就不能电子化,都要扔进垃圾桶了?更何况你一个全是美国人组成的标准小组,也不是很懂中日韩三国文化,也没有知会三国政府,就搞一个标准说我统一了你们的字符你们要听我的?谁给你这个权利。搞出来的字符标准很多人名和地名都打不出来要你何用?我是不是也可以统一英式英语和美式英语然后告诉你你要听我的?因此最初的Unicode标准遭到了中日韩的反对,特别是日本人当时正在和美国人在计算机标准上展开竞争(主要是东京大学主导的TRON开放计算机架构和微软Windows的竞争,多语言字符编码是其中的子项目)更不会容忍一个对本国有诸多缺陷的计算机通用标准

另一方面,自施乐公司和IBM开始搞多语言编码之后,瑞士日内瓦的国际标准组织ISO也开始跟进,也就是和美国平行的第二套多语言编码开发项目。作为一个组织,也许是因为不像美国的公司有迫切的经济利益,设定标准的着眼点是不一样的,应该是考虑到了16位不够用的问题,ISO一开始就是以32位的字符集作为基础,除非以后我们能遇到外星人,不然肯定是够用了,和美国捉襟见肘的16位完全不同。ISO这部分主要是日本和欧洲在主导开发,已经到了接近完成的程度。当然,世界上不需要两个不兼容的编码技术标准,更不要说这个标准本身就是致力于信息的统一交换的,这就涉及一个谁听谁的的问题,美国毕竟是计算机大本营,IBM的通用PC在90年代初已经快统一市场了,上面跑的系统就是Unicode大户微软搞的,当时没落的苹果则是Unicode的另一个大户。美国如果执意对抗ISO,不理睬国际标准,ISO一点办法都没有,就会造成标准的分裂,甚至ISO可能会在标准争夺战中输掉。因此92年,ISO妥协,放弃了最初32位的设想,同时废除了日本和欧洲主导的32位通用编码草案,采取美国的16位Unicode标准,这个标准最终被命名为Unicode 1.1或者ISO/IEC 10646-1: 1993。但在实际使用过程中,16位一下就变得捉襟见肘,我猜是因为中日韩三国的反对,因此这个标准被迫被“升级”,引入了代理对和文本平面的概念,技术细节略微复杂还略微晦涩(例如为什么文本平面是17个这种莫名其妙的数字我也没找到答案),我们只要知道字符集从原先16位对应的UCS-2,被扩充到了32位的UCS-4(其实四个字节占不满,只是出于计算机的惯例用了4个字节)。当然美国并非通吃,其中的很多相互妥协的技术细节我们也暂时不去管它(例如今天Unicode中文本平面这样的概念源自于ISO标准的构想),我们只需要记住,最重要的节点是,一开始Unicode赢了ISO,因此Unicode这个16位的UCS-2字符集——美国的自定标准,从字符集排布到字符集大小,最终还是成了ISO和IEEE承认的全球标准,但是Unicode的容量过小因此进行了升级,最终还是使用了UCS-4,用32个位表示一个字符代码。从字符集的大小角度来讲,ISO的预判是合理的,美国是傲慢的或者短视的,但是从实际角度上来看,其实Unicode就是把ISO标准吃掉了。这也解释了Unicode基本文本平面看起来很支离破碎的原因,CJK汉字由于历史原因要分批加入当然是可以理解的,但是为什么会加的支离破碎的在各种奇奇怪怪的补充平面上?明明Unicode有足够的空间的,为什么不直接分一块整的给汉字?这是因为Unicode最初的版本就只给了汉字两万个空位,后面虽然升级了,但是基本平面已经满了。所以只能加的到处都是

虽然没有找到公开资料表明Unicode最终的拓展是中日韩汉字文化圈的抵制,但是凭借逻辑推断是合理的,Unicode 88标准很清楚的表示了Unicode就是美国的产业标准,里面措辞的自负已经很清楚的表示出了制定者根本懒得管其它民族的文化传承这回事,所以才敢直接搞16位,如果不是中日韩三国(毕竟当年日本厉害,美国人总不可能推行一个世界标准,结果世界第二大市场拒绝使用)的反对,说不定美国不会为非洲或者中亚不知道哪个小国家修改全球性的标准,那么65536个字符下,很多文明的历史,就彻底和电子信息时代say goodbye了。


Unicode与UTF

从直觉上来看,我们觉得字符代码和字符编码应当是一样的,例如ASCII。但是在Unicode里会有缺陷,Unicode字符集解决了通用的问题,但是通用的另一面是编码效率低,Unicode本身的字符集是32位的,按照直觉编码和字符代码一致,那么每个字符都用4个字节传输,那么对欧美而言,一下就要用原来ASCII码时代四倍的资源传输或者保存一样的文档,对中国人而言也是一样的,原先的GB虽然可能在打一些奇怪的字上有缺陷,但是也只要两个字符一个文档,一下大一倍是不可接受的。因此Unicode在推出之后推广相当困难。直到互联网时代的到来,跨语言信息交互空前频繁,Unicode大势所趋,耗费资源的问题也要解决,因此也就有了UTF(事实上UTF中的UTF-8标准在93年就被提出,但是似乎很多程序员都表示这是在互联网时代才兴起的编码方式)

UTF(Universal Transformation Format,通用传输格式),其实就是不改变字符集中各个字符的代码,建立一套新的编码方式,把字符的代码映射成传输时的编码。最主要的任务就是在使用Unicode保持通用性的同时节约流量和硬盘空间。主要分为UTF-8,UTF-16,UTF-32三种,数字表示每次传输,传输多少个位。此处会涉及定长码和变长码的概念,定长码的意思是一个字符编码的长度是固定的,变长码指不同字符占据的编码长度或许是不同的。定长码由于字符长度固定,因此在字符串的字符(有字符索引时)定位速度很快,因为位置是可以直接计算的,而变长码由于字符长度不一,只能从头开始解析所以会慢一些,但是相对来讲节省空间。变长码简约空间,定长码适合检索解析,用哪一种并无优劣,只有权衡。

UTF-8编码中,某些编码是8位的(英语),某些编码是16位的,某些编码是24位(中文),相当于UTF-8在接收时,接到一个编码如果是合法8位编码,就可以直接把它判定为字符,这就给了Unicode字符集在表示英语时和ASCII一样的效率。如果是不合法的,那继续读一个字节,读的两个字节16位如果是合法的,判别为一个字符,再不行继续读下去,只要传输不出错,终归是能读出的。UTF-8这种编码使得每个字符在实际传输过程中占据的大小是不一样的,因此相较于ASCII这样一样的,UTF-8的编码称之为变长码。UTF-8大受欢迎的主要原因就是对欧美而言,在保留Unicode通用性的情况下避免了流量和空间的浪费。因此广泛运用在互联网,统计2016年互联网87%的网页是用UTF-8编码的。

UTF-16编码一样,区别仅仅在于如果字符是在Unicode 1.1定义的那65536个点里面(称之为基本多语言平面),那么只要一次读取就会被判定合法,两个字节就可以判别字符。如果不在,那再读一次,4个字节一定可以解决问题。事实上,UTF-16本质上就是Unicode最早想要用的16位定长码,无非Unicode标准的扩充,从UCS-2到UCS-4,使得它要加入机制来表示不在基本平面的字符,变成了变长码。UTF-16也是非常常用的(虽然看上去像是美国强推自己Unicode时候定死16位,最后扩充到32位,但是为16位写好的程序又改不了了产生的后遗症),Windows默认的Unicode编码的保存方案,就是UTF-16。在保存数据方面,在保存英语上,UTF-16要两倍于UTF-8的空间,在保存中文时,由于Unicode一开始加入的两万多个汉字基本已经涵盖了所有的常用字,其实补充平面是极少用的,因此一般可以视为两个字节表示一个汉字,效率相较UTF-8要高一些。UTF-32,位数和字符集大小一致,则很显然能一次解析出字符,因此也是目前唯一的定长码,但是浪费就很严重。


主要参考资料

Wikipedia Unicode  https://en.wikipedia.org/wiki/Unicode

Wikipedia UTF-8     https://en.wikipedia.org/wiki/UTF-8

Unicode 88 by Joseph D. Becker  http://www.unicode.org/history/unicode88.pdf

A Brief History of Character Codes in North America, Europe, and East Asia by Steven J. Searle  http://tronweb.super-nova.co.jp/characcodehist.html

Unicode Revisited by Steven J. Searle  http://tronweb.super-nova.co.jp/unicoderevisited.html

Wikipedia 中日韩统一表意文字 https://zh.wikipedia.org/wiki/中日韩统一表意文字

知乎问题 Unicode和UTF-8有何区别 于洋的回答 https://www.zhihu.com/question/23374078

0 0
原创粉丝点击