ASCII 编码,将英文存储到计算机
怎么将二进制与文字对应起来?
计算机时以二进制的形式存储在计算机的,它只认识0和1两个数字,在屏幕上看到的文字,在存储之前都被转换成了二进制(0和1序列),在显式时也要根据二进制找到对应的字符。
特定的文字必然对应着固定的二进制,否则在转换时将发生混乱。那么,怎样将文字与二进制对应起来呢?这就需要有一套规范,计算机公司和软件开发者都必须遵守,这样的一套规范就称为字符集(Character Set)或者字符编码(Character Encoding)。
字符集为每个字符分配一个唯一的编号
可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
ASCII编码
ASCII 是“American Standard Code for Information Interchange”的缩写,翻译过来是“美国信息交换标准代码”。
字符编码 | 说明 |
---|---|
ISO/IEC 8859 | 欧洲字符集,支持丹麦语、荷兰语、德语、意大利语、拉丁语、挪威语、葡萄牙语、西班牙语,瑞典语等,1987 年首次发布。ASCII 编码只包含了基本的拉丁字母,没有包含欧洲很多国家所用到的一些扩展的拉丁字母,比如一些重音字母,带音标的字母等,ISO/IEC 8859 主要是在 ASCII 的基础上增加了这些衍生的拉丁字母。 |
Shift_Jis | 日语字符集,包含了全角及半角拉丁字母、平假名、片假名、符号及日语汉字,1978 年首次发布。 |
Big5 | 繁体中文字符集,1984 年发布,通行于台湾、香港等地区,收录了 13053 个中文字、408个普通字符以及 33 个控制字符。 |
GB2312 | 简体中文字符集,1980 年发布,共收录了 6763 个汉字,其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。 |
GBK | 中文字符集,是在 GB2312 的基础上进行的扩展,1995 年发布。GB2312 收录的汉字虽然覆盖了中国大陆 99.75% 的使用频率,满足了基本的输入输出要求,但是对于人名、古汉语等方面出现的罕用字(例如朱镕基的“镕”就没有被 GB2312 收录),GB2312 并不能处理,所以后来又对 GBK 进行了一次扩展,形成了一种新的字符集,就是 GBK。GBK 共收录了 21886 个汉字和图形符号,包括 GB2312 中的全部汉字、非汉字符号,以及 BIG5 中的全部繁体字,还有一些生僻字。 |
GB18030 | 中文字符集,是对 GBK 和 GB2312 的又一次扩展,2000 年发布。GB18030 共收录 70244 个汉字,支持中国国内少数民族的文字,以及日语韩语中的汉字。 |
如何存储
标准 ASCII 编码共包含了 128 个字符,用一个字节就足以存储(实际上是用一个字节中较低的 7 位来存储),而日文、中文、韩文等包含的字符非常多,有成千上万个,一个字节肯定是不够的(一个字节最多存储 28 = 256 个字符),所以要进行扩展,用两个、三个甚至四个字节来表示。
在制定字符编码时还要考虑内存利用率的问题。我们经常使用的字符,其编码值一般都比较小,例如字母和数字都是 ASCII 编码,其编码值不会超过 127,用一个字节存储足以,如果硬要用多个字节存储,就会浪费很多内存空间。
为了达到「既能存储本国字符,又能节省内存」的目的,Shift-Jis、Big5、GB2312 等都采用变长的编码方式:
- 对于原来的 ASCII 编码部分,用一个字节存储足以;
- 对于本国的常用字符(例如汉字、标点符号等),一般用两个字节存储;
- 对于偏远地区,或者极少使用的字符(例如藏文、蒙古文等),才使用三个甚至四个字节存储。
总起来说,越常用的字符占用的内存越少,越罕见的字符占用的内存越多。
中文编码方案
GB2312 --> GBK --> GB18030 是中文编码的三套方案,出现的时间从早到晚,收录的字符数目依次增加,并且向下兼容。GB2312 和 GBK 收录的字符数目较少,用 1~2个字节存储;GB18030 收录的字符最多,用1、2、4 个字节存储。
1)从整体上讲,GB2312 和 GBK 的编码方式一致,具体为:
- 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII 编码是一致的,所以说 GB2312 完全兼容 ASCII。
- 对于中国的字符,使用两个字节存储,并且规定每个字节的最高位都是 1。
2)GB18030 为了容纳更多的字符,并且要区分两个字节和四个字节,所以修改了编码方案,具体为:
- 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII、GB2312、GBK 编码是一致的。
- 对于常用的中文字符,使用两个字节存储,并且规定第一个字节的最高位是 1,第二个字节的高位最多只能有一个连续的 0(第二个字节的最高位可以是 1 也可以是 0,但是当它是 0 时,次高位就不能是 0 了)。注意对比 GB2312 和 GBK,它们要求两个字节的最高位为都必须为 1。
- 对于罕见的字符,使用四个字节存储,并且规定第一个和第三个字节的最高位是 1,第二个和第四个字节的高位必须有两个连续的 0。
字符处理软件在处理文本时,从左往右依次扫描每个字节:
-
如果遇到的字节的最高位是 0,那么就会断定该字符只占用了一个字节;
-
如果遇到的字节的最高位是 1,那么该字符可能占用了两个字节,也可能占用了四个字节,不能妄下断论,所以还要继续往后扫描:
1.如果第二个字节的高位有两个连续的 0,那么就会断定该字符占用了四个字节;
2.如果第二个字节的高位没有连续的 0,那么就会断定该字符占用了两个字节。
可见,当字符占用两个或者四个字节时,GB18030 编码要检测两次,处理效率比 GB2312 和 GBK 都低。
Unicode字符集
Unicode 也称为统一码、万国码;
严格来说,字符集和字符编码不是一个概念:
- 字符集定义了字符和二进制的对应关系,为每个字符分配了唯一的编号。可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
- 而字符编码规定了如何将字符的编号存储到计算机中。如果使用了类似 GB2312 和 GBK 的变长存储方案(不同的字符占用的字节数不一样),那么为了区分一个字符到底使用了几个字节,就不能将字符的编号直接存储到计算机中,字符编号在存储之前必须要经过转换,在读取时还要再逆向转换一次,这套转换方案就叫做字符编码。
Unicode 可以使用的编码方案有三种,分别是:
- UTF-8:一种变长的编码方案,使用 1~6 个字节来存储;
- UTF-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;
- UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。
UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。
- UTF-8
- 如果只有一个字节,那么最高的比特位为 0,这样可以兼容 ASCII;
- 如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。
具体的表现形式为:
- 0xxxxxxx:单字节编码形式,这和 ASCII 编码完全一样,因此 UTF-8 是兼容 ASCII 的;
- 110xxxxx 10xxxxxx:双字节编码形式(第一个字节有两个连续的 1);
- 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(第一个字节有三个连续的 1);
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(第一个字节有四个连续的 1)。
2) UTF-32
UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提高了效率。
3) UTF-16
UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储。
对比以上三种编码方案
首先,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。
- UTF-8 使用尽量少的字节来存储一个字符,不但能够节省存储空间,而且在网络传输时也能节省流量,所以很多纯文本类型的文件(例如各种编程语言的源文件、各种日志文件和配置文件等)以及绝大多数的网页(例如百度、新浪、163等)都采用 UTF-8 编码。
UTF-8 的缺点是效率低,不但在存储和读取时都要经过转换,而且在处理字符串时也非常麻烦。例如,要在一个 UTF-8 编码的字符串中找到第 10 个字符,就得从头开始一个一个地检索字符,这是一个很耗时的过程,因为 UTF-8 编码的字符串中每个字符占用的字节数不一样,如果不从头遍历每个字符,就不知道第 10 个字符位于第几个字节处,就无法定位。
不过,随着算法的逐年精进,UTF-8 字符串的定位效率也越来越高了,往往不再是槽点了。
- UTF-32 是“以空间换效率”,正好弥补了 UTF-8 的缺点,UTF-32 的优势就是效率高:UTF-32 在存储和读取字符时不需要任何转换,在处理字符串时也能最快速地定位字符。例如,在一个 UTF-32 编码的字符串中查找第 10 个字符,很容易计算出它位于第 37 个字节处,直接获取就行,不用再逐个遍历字符了,没有比这更快的定位字符的方法了。
但是,UTF-32 的缺点也很明显,就是太占用存储空间了,在网络传输时也会消耗很多流量。我们平常使用的字符编码值一般都比较小,用一两个字节存储足以,用四个字节简直是暴殄天物,甚至说是不能容忍的,所以 UTF-32 在应用上不如 UTF-8 和 UTF-16 广泛。
- UTF-16 可以看做是 UTF-8 和 UTF-32 的折中方案,它平衡了存储空间和处理效率的矛盾。对于常用的字符,用两个字节存储足以,这个时候 UTF-16 是不需要转换的,直接存储字符的编码值即可。
宽字符和窄字符(多字节字符)
有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。
有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符。
Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。