编程知识 | 浅谈编码格式

1,126 阅读8分钟

编程知识.png

相关推荐:

0. Thanks

1. 概述

在日常编(ban)码(zhuan)的过程中,我们经常会听到,UTF-8,GBK,Unicode,ASCII 等等的编码格式,我们应该都知道他们是编码格式,但其有什么区别?其原理是什么呢?我们来稍微学习一下。

2. ASCII

在计算机的世界里面,所有的信息都是一个二级制的直,每一个二进制(bit)有 0 和 1 两种状态,而在人类的世界里,却有很多语言,以英语为例,基本的就有26个字母,字母之上就是单词,如果想用二进制来表达单词,展示文章,我们需要建立其,二级制和26个字母之间的映射关系。

这是我理解的 ASCII ,ASCII 不单单只有英文字母,还包括了标点符号等基本字符。来个官方的介绍:

上个世纪 60 年代,美国制定了一套基于拉丁字母的计算机编码系统,用于显示现代英文。称为 ASCII(American Standard Code for Information Interchange,美国信息交换标准代码),一直沿用至今。

ASCII 规定了适用 8 个二进制位来表示,8个bit就是一个字节,ASCII 规定,字节最前面一位统一规定为0,后面7位来表示具体的信息。例如,

字符 A,就是用 0100 0001 来表示,详细的映射可以看 ASCII码对照表

3. GBK

ASCII 是美国的标准,其解决了英文的编码问题,但是我们的中文呢?我们的汉字可是有很多,8 个bit已经远远满足不了我们,所以我们先定义了GB2312。GB2312是简体中文常见的编码方式,使用两个字节表示一个汉字,所以最多可以表示2^16 = 65536个符号。

GB2312标准共收录6763个汉字,其中一级汉字(常用字)3755个,二级汉字(较不常用)3008个,同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。

GB2312基本满足了计算机处理简体汉字的需求,所收录的汉字覆盖了99.75%的使用频率,但对于罕见字和繁体字,GB2312就不能处理了。因此发明了后来的GBK和GB18030。

4. Unicode

GBK 是解决了中文的编码方式,但是世界上的其他语言呢?怎么办?难道每一种语言都搞一个自己的编码方式么?这时候,一个通用的编码方案设想就被提出来了,其就是 Unicode。

Unicode是一个编码方案,Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 编码共有三种具体实现,分别为utf-8,utf-16,utf-32。

4.1 UTF-8

UTF-8 是 Unicode 编码方案的实现方式之一,也是最为广泛适用的一种 Unicode 编码方案。UTF-8的特点是对不同范围的字符使用不同长度的编码,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

世界上的字符,有一些可以用一个字节来表示,而有一些则需要多个字节来表示。UTF-8 是这样来定义编码规则的:

  • 单字节字符,字节第一位设为 0,后面 7 位位这个符号的 Unicode 码。对于英语字母,UTF-8 和 ASCII 码是相同的。
Unicode范围Unicode范围UTF-8
U+0000~U+007F0000 0000 - 0000 007F0xxx xxxx
  • 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码。
Unicode范围Unicode范围n字节符号UTF-8
U+0080~U+07FFU+ 0000 0080 - 0000 07FF2110x xxxx 10xx xxxx
U+0800~U+FFFF0000 0800 - 0000 FFFF31110 xxxx 10xx xxxx 10xx xxxx
U+010000~U+10FFFF0001 0000 - 0010 FFFF41111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx

来举个栗子,如汉字严,从 Unicode 的 编码网址 中查到,严字的 Unicode 为:U+4E25,从上面的表格,U+4E25(100111000100101) 处于 0000 0800 - 0000 FFFF,需要用 3 个字节符号来表示,其内容要塞到xxx中,那么:

简单来说,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。剩下的都是 Unicode 的编码信息,从右边开始,不够的前面补零。

4.2 UTF-32

相比 UTF-8 的变长编码方式,UTF-32 就显得粗暴得多了,其用四个字节(32bit)对Unicode编码字符进行存储,这就是UTF-32。UTF-32是最简单的程序实现方案(无需转换,与Unicode编码一一对应)。但是这带来一个非常明显的坏处:浪费空间

4.3 UTF-16

UTF-16 也是一个变长的编码方式,将Unicode分为了两个范围,分别通过不同的方式进行存储。

Unicode范围Unicode范围UTF-16
U+000~U+FFFF0000 0080 - 0000 FFFF2 Byte存储,编码后等于Unicode值
U+10000~U+10FFFF0001 0000 - 0010 FFFF4 Byte存储,现将Unicode值减去(0x10000),得到20bit长的值。再将Unicode分为高10位和低10位。UTF-16编码的高位是2 Byte,高10位Unicode范围为0-0x3FF,将Unicode值加上0XD800,得到高位代理(或称为前导代理,存储高位);低位也是2 Byte,低十位Unicode范围一样为0~0x3FF,将Unicode值加上0xDC00,得到低位代理(或称为后尾代理,存储低位)

来举些例子吧,对于在第一个范围内的 Unicode ,Utf-16 是直接编码,例如汉子:严,Unicode 是 U+4E25,处于 U+000~U+FFFF ,根据编码规则,其 UTF-16 为 :0x4E25

对于范围2,举个这样的例子:

Unicode 为 U+12345,位于 U+10000~U+10FFFF,那么编码成 UTF-16,

  1. 先把 U+12345 减去 0x10000,0x12345 - 0x10000 = 0x2345。
  2. 0x2345 其二进制为:10 0011 0100 0101,拆分成高10位和低10位bit,那么,高:10 00,低:11 0100 0101,高位不足10位就补零。
  3. 高位:10 00,0x8,加上 0xD800,得到高代理:0xD808
  4. 地位:11 0100 0101,0x345,加上 0xDC00,得到低代理:0xDF45
  5. 综合,Unicode: U+12345 的 UTF-16 为 D808DF45

和网站的结果一致:

5. 其他补充

  1. 在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。

  2. 统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

  3. Windows下中文的默认编码是GBK(GB2312),Linux下中文的默认编码是UTF-8,Android 和 JVM 用的是 UTF-8。


码字不易,方便的话素质三连,或者关注我的公众号 技术酱,专注 Android 技术,不定时推送新鲜文章,如果你有好的文章想和大家分享,欢迎关注投稿!

技术酱