什么是字符编码

在计算机中,所有信息最终都是用二进制来表示的。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,从0000000011111111

关于byte还有个单位换算:1024byte表示1KB,1024KB表示1MB,1024MB表示1GB,1024GB表示1TB,1024TB表示1PB。

计算机于1946年2月15日诞生于美国的宾夕法尼亚大学,起名叫做ENIAC,这部计算机体积非常的庞大,占满一整个房间,耗电量也非常高,当有人使用它的时候,全镇的人都会知道,因为他们家的电灯都会变暗。

ASCII 码

当时为了在计算机中表示英文中的字符,美国制定了一套字符编码,对英文字符与二进制位之间做了映射关系,这被称为 ASCII 码:

什么是字符编码_第1张图片

其中的编号从0~32的字符,被赋予特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作:

遇上0x10, 终端就换行
遇上0x07, 终端就向人们嘟嘟叫
遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。

这些字符就称为”控制码”。

GB2312

拥有了ASCII码之后,英文就可以在计算机当中完美的表示出来了,但随着计算机的普及,其他的国家也要求将自己国家的语言编入计算机当中,他们纷纷在基于ASCII码之上,编写了自己的码表,比如中国的GB2312。

GB2312规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。

Unicode

因为各个国家都编写了自己的码表,导致同一个二进制数字可以被解释成不同的符号,对信息的交流带来了不变,特别是邮件传输的时候,经常会出现乱码。

为了解决这个问题,ISO(国际标谁化组织)决定着手解决这个问题,他们呼吁各个国家不要在编写新的码表,然后他们制作了Unicode,全称是:Universal Multiple-Octet Coded Character Set,全球统一字符编码集。

注意Unicode只是单纯的规定了数字和字符之间的关系,比如规定了20064表示汉字:习,但是Unicode并没有规定改如何在计算机中存储信息。

UTF-8

UTF表示Unicode TransferFormat,UTF-8是一种 Unicode 的实现方式,也是在互联网上使用最广的实现方式,其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode 码。

下表总结了编码规则,字母x表示可用编码的位:

Unicode符号范围      |        UTF-8编码方式
(十六进制)           |              (二进制)
--------------------+-------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字严为例,演示如何实现 UTF-8 编码。

的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此的UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx,然后,从的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0,这样就得到了,的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5

UTF-8的缺点

1、对于欧美地区一些以英语为母语的国家 UTF-8 简直是太棒了,因为它和 ASCII 一样,一个字符只占一个字节,没有任何额外的存储负担;但是对于中日韩等国家来说,UTF-8 实在是太冗余,一个字符竟然要占用 3 个字节,存储和传输的效率不但没有提升,反而下降了。所以欧美人民常常毫不犹豫的采用UTF-8,而我们却需要要犹豫一下。

2、变长字节表示带来的效率问题,因为UTF-8是变长字节表示,因此无论是计算字符数,还是执行索引操作效率都不高。为了解决这个问题,常常会考虑把 UTF-8 先转换为 UTF-16 或者 UTF-32 后再操作,操作完毕后再转换回去。而这显然是一种性能负担。

UTF-16

UTF-16使用两个或者四个字节来存储所有的字符,一开始是使用两个,但是我们知道,两个字节最多只能够存储65536(0x0000~0xFFFF)个字符,所以后来改版,对其进行了扩展,规定,小于65536(0xFFFF)的用两个字节表示,否则就用四个字节表示。

UTF-16很明显的一个问题是,对于像ASCII中的可以使用一个字节来存放的字符,也使用两个或者四个字节来存放,空间浪费很大,UTF-16的另外一个问题是存在大端小端的问题。

大端小端

以汉字严为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,这就是Big endian 方式;25在前,4E在后,这是Little endian 方式。

这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-endian)敲开还是从小头(Little-endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。

第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是“小头方式”(Little endian)。

那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?

Unicode 规范定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做“零宽度非换行空格”(zero width no-break space),用FEFF表示。这正好是两个字节,而且FFFE1

如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

你可能感兴趣的:(什么是字符编码)