ASCII,Unicode 和 UTF-8 的故事

阅读 462
收藏 19
2017-06-16
原文链接:chunge2016.online

前言:

相信很多人都听说过ASCIIUnicodeUTF-8这些词汇,但可能还是不清楚其中的具体含义。当然一开始我也是粗略认识,所以自己决定重新深入学习一下,并且总结如下。

才疏学浅,多多指教!!!

一、历史渊源

关于历史来源,在此引用前人的总结,写的很清晰明了。自己截取其中主要部分并且加以修饰一下,正文如下:

很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,每个于是他们把这称为”字节“。再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为”计算机“。这就是计算机来源,每个晶体管只有0和1两个状态。现在计算机,8位二进制通常被称为一个字节,这是计算机最小存储单位。

计算机是美国人发明的。八位二进制一共可以组合出256(2的8次方)种不同的状态。 他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0×10, 终端就换行,遇上0×07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0×20以下的字节状态称为控制码。他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的Ascii编码 (American Standard Code for Information Interchange,美国信息互换标准代码) 当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。

世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称扩展字符集

等到中国人民使用计算机后,发现没有中文怎么办呢?中国人的解决方案是:小于127号的还是继续使用,并且用2个大于127的字节表示一个中文字符,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的 全角 字符,而原来在127号以下的那些就叫 半角 字符了。 中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。

但是中文还是不够用,大家发现冷门生僻字和繁体字等等还是无法识别。于是就只是要求高字节大于127就认为是2字节的中文字符,这样结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。但是还有少数民族同胞也需要使用电脑,于是有扩展少数民族字符,这样GBK扩成了 GB18030,从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 DBCS(Double Byte Charecter Set 双字节字符集)。

但是每个国家都搞一套自己的字符编码,自己只能看自己,别人的看不来,这不符合web开放的文化啊。

正在这时一个叫 ISO(国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 Unicode。但是后来扩展标准,2或者4字节来表示字符编码。

Unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用 两个字节 ,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前最新Unicode标准分为17组编排,0x0000 至 0xFFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112(65536*17)个。

但是Unicode同样也有2个问题

一、计算机怎么知道二个字节为一个字符,如何识别二个字节为什么一个字符?

二、针对英文字符,如果使用大于1个字节来表示,那么低位的前面几个字节全是0。很奢侈浪费空间,因为现在计算机大部分内容还是英文。

历史大致发展

二、Charset and Encoding

大家了解一下历史后,应该对字符集有大致了解,那我们来说说字符集和字符编码,其实你可以这样理解:

字符集就是我们用十进制来表示世界上各种字符

字符编码就是通将十进制转换为计算机识别的二进制编码的规则

  • Charset (Character set) 字符集: 是对字符抽象表示的集合。包括世界上各种文字、符合和字符。

  • Encoding (Charset Encoding) 字符编码:建立字符集合和计算机系统对应的规则。简单来说就是,将字符转化为计算机可识别的二进制编码的规则。

Charset and Encoding

三、ASCII

计算机使用二进制保存指令和数据,字节作为计算接处理数据的基本单位,有256(2的8次方)种可能状态。这些状态被人们利用来标记指令和文字。最初前32(0x20)种状态被用来表示终端、打印机等设备的某些特殊动作,比如终端遇到字节0x10则换行,类似的还有回车(CR 0x0D),震铃(BELL 0x07)等。这前32种状态又被称为控制码。除了这些特殊用途的状态外,还有256-32=224种状态没有被利用,资源浪费怎么能忍,何况除了控制指令外,咱们的文字还没有被表示。于是这些多出来的状态被用来表示文字(English Character)和符号标点等,如此这般,计算机就可以显示和记录文字了,这便是ASCII(American Standard Code for Information Interchange)最初的由来。

四、Unicode

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

在表示一个Unicode的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在基本多文种平面(英文为 Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要用四位十六进制数(例如U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五位或六位十六进制数了。Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,0x0000 至 0xFFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。

通用字符集(Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4用4个字节编码。

UCS-4根据最高位为0的最高字节分成27=128个group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行 (row),每行有256个码位(cell)。group 0的平面0被称作BMP(Basic Multilingual Plane)。如果UCS-4的前两个字节为全零,那么将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。每个平面有216=65536个码位。Unicode计划使用了17个平面,一共有17×65536=1114112个码位。

UTF(Unicode Transformation Format), Unicode字符集的编码方式,主要有UTF-8,UTF-16,UTF-32等。由于UTF-32采用定长四字节编码,这里不多说,下面主要介绍UTF-8,附带介绍UTF-16。

UTF-8

UTF-8是变长编码,使用1~4个字节来表示一个字符,UTF-8的特点是对不同范围的字符使用不同长度的编码。

单字节表示对应Unicode码点0x0000-0x007F的字符,且此时首位为0,有效位7位,也就是说可以表示27=128种字符,这部分其实刚好兼容了ASCII编码规则,而Unicode0x0000-0x007F的字符也就是ASCII字符。

双字节表示对应Unicode码点0x0080-0x07FF的字符,且此时第一个字节以110开头,后面的字节以10开头,有效 位数为11位,可以表示2048种字符。其实由上图可以看到UTF-8变长编码的规律,为了让计算机知道以几个字节来断句(即翻译为一个字符),在首字节的前几位就有几个1,例如如果是双字节断句,则首字节开头以110,如果是三字节断句,则首字节以1110开头。

三字节表示对应Unicode码点0x0800-0xFFFF的字符,有效位为16位,可以容纳65536种字符,我们常用的中文字符也都落在三字节部分。因此UTF-8是以三个字节来表示常用汉字字符的,当然实际上汉字有将近十万个,65536无法容纳如此庞大的汉字体系,因此有些冷门汉字只能用四字节表示了。我们知道Unicode将人们常用的字符都放在BMP平面(U+0000-U+FFFF),而至此,UTF-8已经用1-3个字节表示了BMP平面的全部内容。

例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

UTF-16

UTF-16是以二字节或四字节表示一个字符的变长编码。Unicode在BMP平面的字符(U+0000~U+FFFF)用二字节来编码,其余字符用四字节来编码。对于二字节编码部分直接用码点值来编码,四字节编码部分的转换规则这里不再详述。我们只需记住常用的中英文字符在UTF-16中使用二字节表示即可。

乱码

编码方式不兼容会导致乱码。例如,编码A和编码B采用不同方式来编码,当一个文件使用编码A在只有编码B的设备上解码时候,肯定是乱码,因为不识别啊。打个比方,抗张谍报的密码本,每次传递情报需要使用不同的密码本来解密,但是你用错了,当然翻译的内容是错乱的,读起来肯定不通顺。

文笔有限,才疏学浅,文中若有不对之处,还望告知。

参考链接

评论