在计算机内部,所有的数据都是以二进制0、1表示,每个二进制位为一个比特(Bit),但是一个Bit能表示的信息太少,所以一个最小的操作单位应该包含多个多个比特,起初这个单位并没有统一标准,有4比特一个单位的,也有7个、8个比特一个单位的,但是最终还是8比特单位一统江湖,这就有了后来的 1Byte= 8Bit。
为什么需要对字符编码呢?如果没有字符编码,通过计算机进行聊天时,可能是这样的:A->B:01101000 01101001,B收到信息之后可能就需要查字典去了,那为什么计算机不帮我查好呢!所以编码就是二进制与字符的转换关系。
ASCII编码
1967年美国制定了一套编码规范,其中规定了 128 个字符与二进制的对应关系,称之为 ASCII 编码,二进制范围是 0-127:[00000000,011111111],可以看出来最高位没有使用。
虽然 ASCII 规范出来了,但是存在很多不足,主要原因是它所包含的字符集太小,只有拉丁字母、阿拉伯字符、英文标点和一些控制字符,此时欧洲人第一个站出来表示不高兴,比如意大利语的字符 Á 没法表示,正好ASCII 编码中最高位被闲置了,把这部分进行利用,将ASCII 从7位扩展成了8位,这就是扩展ASCII(EASCII)。
将 ASCII 编码扩展之后,欧洲人高兴了,但是其他语音还是不能处理。为了解决这些语言的编码问题,各国开始搞自己的编码,这期间产生了许多种编码格式,比如常见的汉字编码格式 GB-2312。各个国家都采用不同的编码,这样是没办法相互通信的,所以需要有一种编码,能够涵盖所有语言符号,这就是 Unicode。
Unicode 编码
Unicode 也叫万国码、统一码、国际码,它是为了解决各个编码格式的不兼容问题,它为各个语言字符进行了编号,比如汉字“你”编号为 0x4f60,“好”编号为 0x597d。
Unicode 只是对各个语言字符进行了编号,但是没有对编号如何存储、存储几个字节进行规定,还是以汉字“你”为例,它的编号是 0x4f60,最少可以用两个字节来存储,也可以用3个、4个;另一个问题是计算机怎么判断这两个字节是一个Unicode字符还是两个ASCII字符呢。基于这些问题,产生了多种 Unicode 字符集实现格式,其中就包括 UTF-8.
UTF-8 编码实现
UTF-8 全称是 8 Bit Unicode Transformation Format,UTF-8是Unicode编码的具体实现方式之一,它最主要的优点就是可变长度,字符编码通常由 1-4 个字节来表示(存在5、6个字节的字符,但是不在 Unicode 字符集中),相比定长的实现方式,能够节省更多的空间。
针对任意字符编码,它的编码规则如下:
- 对于单字节字符,字节的最高比特位设置为0。
- 多字节序列的首字节的最高有效位决定了字节序列的长度,最高有效位有几个连续的 1,表示字节序列多长,比如首字节为 11000000 表示是2个字节,11100000 表示3个字节,11110000表示4个字节。
- 多字节序列除首字节遵守规则2之外,其他字节最高有效位为10。
根据上面的规则,对各个长度的字节序列总结如下:
字节序列 | 码点位数 | 码点起值 | 码点终值 | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|---|---|---|
1 | 7 | U+0000 | U+007F | 0xxxxxxx | |||
2 | 11 | U+0080 | U+07FF | 110xxxxx | 10xxxxxx | ||
3 | 16 | U+0800 | U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx | |
4 | 21 | U+10000 | U+1FFFFF | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
对于UTF-8编码中的任意字节B,如果B的第一位为0,则B独立的表示一个字符(ASCII码);
- 如果B的第一位为1,第二位为0,则B为一个多字节字符中的一个字节(非ASCII字符);
- 如果B的前两位为1,第三位为0,则B为两个字节表示的字符中的第一个字节;
- 如果B的前三位为1,第四位为0,则B为三个字节表示的字符中的第一个字节;
- 如果B的前四位为1,第五位为0,则B为四个字节表示的字符中的第一个字节;
因此,对UTF-8编码中的任意字节,根据第一位,可判断是否为ASCII字符;根据前二位,可判断该字节是否为一个字符编码的第一个字节;根据前四位(如果前两位均为1),可确定该字节为字符编码的第一个字节,并且可判断对应的字符由几个字节表示;根据前五位(如果前四位为1),可判断编码是否有错误或数据传输过程中是否有错误。
关于BOM
通常在保存UTF-8的时候,这里以 NotePad++ 为例, 会出现两个可选项:UTF-8,UTF-8-BOM,那么这两个选项有什么区别呢?很明显就是一个带BOM、一个不带BOM。BOM全名是字节顺序标记(Byte-Order-Mark),它存储在文件的开头,用来表示文件的字节顺序是大端还是小端,BOM通常是一个 U+FEFF 字符,UTF-8 一般是 EF、BB、BF。
对于 UTF-8 来说,BOM是完全没有存在的必要的,因为 UTF-8 是直接操作的单个字节,而非操作多字节整形(比如C的int),所以不存在大小端问题,UTF-8 文件的 BOM 作用只是表示这个文件是一个 Unicode 编码文件。
对于需要进行网络传输的 UTF-8 文件来说,谨慎使用 BOM,因为它会影响字符内容的解析,当从数据流解析出来的 UTF-8 字符在开头出现乱码,那么八成是 BOM 引起的。
最后需要提醒的是,使用 Windows 的记事本存储为 UTF-8 格式的时候,默认是添加 BOM 头的!!!所以珍爱生命,远离记事本!