赞
踩
我们使用计算机处理字符(字母、数字、标点、文字等)类型的信息,首先,需要计算机能够存储字符。我们知道计算机处理的数据只能是二进制串,并不能直接存储字符。因此,需要将字符转化为二进制串。为了方便转化,这里需要介绍两个概念:字符集和编码规则。
字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point)
编码规则:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
例如,Unicode是字符集,编码规则:UTF-8、UTF-16、UTF-32。如果我们说Unicode编码,广意上它其实包含了字符集和各种编码规则。在ASCII编码中,字符A对应的数字是65,65称字符A的码点。
对于 ASCII 字符集,ASCII 总共包含 128 个字符,用 7 个比特位(Bit)恰好能够存储,其编码规则,就是存储码点对应的二进制数。
但是对于 Unicode,问题就没有这么简单了。Unicode 目前已经包含了上百万的字符,位置靠前的字符用一个字节就能存储,位置靠后的字符用三个字节才能存储。我们可以为所有字符都分配三个字节,也可以为编号小的字符分配一个字节或者两个字节,而为编号大的字符分配三个字节。
这两种方案各有优缺点,请读者看下面的分析。
字符集和字符编码不是一个概念,字符集定义了文字和二进制的对应关系,为字符分配了唯一的编号,而字符编码规定了如何将文字的编号存储到内存中。有的字符集在制定时就考虑到了编码的问题,是和编码结合在一起的;有的字符集只管制定字符的编号,至于怎么编码,是其他人的事情。
一种方案是为每个字符分配固定长度的内存,并且这块内存要足够大,可以容纳下所有的字符编号。这种方案最简单,直接将字符编号放入内存中即可,不需要任何转换,并且以后在字符串中定位字符、修改字符都非常容易。
字符串就是一串连续的字符序列,它们在内存中按次序挨着存放。在C语言中,字符串由双引号 " "包围起来。
目前的 Unicode 已经收录了上百万的字符,至少需要三个字节才能容纳下所有的字符编号。假设字符串"A3中¥"的 Unicode 编码值(十六进制形式)分别是 2A、31、DA49、BB672C,那么它们在内存中的存储形式为:
在几乎所有的字符集中,常用字符的编号往往比较小,罕见字符的编号往往比较大,包括 Unicode 在内。
A和3是 ASCII 编码中的字符,Unicode 为了兼容 ASCII,在设计时刻意保留了原来 ASCII 中字符的编号,所以英文字母和阿拉伯数字在 Unicode 中的编号都非常小,用一个字节足以容纳。中是一个汉字,编号比较大,一般要用两个字节才能容纳。¥可以看做是一个极其少见,或者只有极少数地区才会使用到的字符,这样的字符编号往往比较大,有时候需要三个字节才能容纳。
¥是人民币符号,是汉字文化的一部分,它和其它汉字一样,实际上是用两个字节存储的,不过这里我们为了演示,故意犯错地说它需要三个字节。
上图中带灰色背景的字节是没有用到的字节,它们就是被浪费掉的一部分内存空间,这就是用固定长度的内存来存储字符编号的缺点:常用字符的编号都比较小,这种方案会浪费很多内存空间,对于以英文为主的国家,比如美国、加拿大、英国等,内存利用率甚至会低于 50%。
既然上面的方案有缺点,那我们就来改进一下。改进的思路也很明确,就是把空闲的内存压缩掉,为每个字符分配尽量少的字节,例如,A和3分配一个字节足以,中分配两个字节足以,如下图所示:
这样虽然没有了空闲字节,不浪费任何内存空间了,但是又出现新的问题了:如果我不告诉你,你怎么知道2A表示一个字符,而不是2A31或者2A31DA才表示一个字符呢?后面的字符也有类似的问题。
对于第一种方案,每个字符占用的字节数是固定的,很容易区分各个字符;而这种方案,不同的字符占用的字节数不同,字符之间也没有特殊的标记,计算机是无法定位字符的。
这种方案还需要改进,必须让不同的字符编码有不同的特征,并且字符处理程序也需要调整,要根据这些特征去识别不同的字符。
要想让不同的字符编码有不同的特征,可以从两个方面下手:
例如,对于编号较小的、用一个字节足以容纳的字符,我们就可以规定这个字符编号的最高位(Bit)必须是 0;对于编号较大的、要用两个字节存储的字符,我们就可以规定这个字符编号的高字节的最高位必须是 1,低字节的最高位必须是 0;对于编号更大的、需要三个字节存储的字符,我们就可以规定这个字符编号的所有字节的最高位都必须是 1。
程序在定位字符时,从前往后依次扫描,如果发现当前字节的最高位是 0,那么就把这一个字节作为一个字符编号。如果发现当前字节的最高位是 1,那么就继续往后扫描,如果后续字节的最高位是 0,那么就把这两个字节作为一个字符编号;如果后续字节的最高位是 1,那么就把挨着的三个字节作为一个字符编号。
这种方案的缺点很明显,它会导致字符集不连续,中间留出大量空白区域,这些空白区域不能定义任何字符。
那么,转换后的编号要具备什么样的特征呢?其实也可以像上面一样,根据字节的最高位是 0 还是 1 来判断字符到底占用了几个字节。
相比第一种方案,这种方案有缺点也有优点:
缺点就是多了转换过程,字符在存储和读取时要经过转换,效率低;
优点就是在制定字符集时不用考虑存储的问题,可以任意排布字符。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。