Skip to content

常见的字符集与编码方案的认识 #22

Description

@pekonchan

背景

在计算机中,所有的数据在存储和运算时都要使用二进制数表示,例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则。

下面分为两个部分来说明一些常见的概念。

字符集是上面说的字符和二进制一一对应的一个关系表,当然为了方便阅读,把二进制表示成十进制,形成了字符和数字的一个个对应关系表。根据这个表,我们就可以知道一个字符在计算机里是以什么二进制来表示的。

编码方案,我们理解为是依据编码表,来落实转化操作的一个实施方案,毕竟有制定了规则,也是得需要落实规则的方案去执行对吧。所以是将数字转换到程序数据的编码方案

字符集

ASCII

最初计算机大多数为美国和西欧所使用,他们为了把一些字符统一以二进制来表示,作为计算机所能识别的资源,以方便相互认识和交流,制定出了ASCII码,美国标准信息交换代码(American Standard Code for Information Interchange)的缩写, 为美国英语通信所设计。

ASCII分为标准(基础)ASCII和扩展ASCII。前者是以7位二进制表示(虽然一个字节还是8bit,但是最高位是不用的),即能表示2^7=128个字符,表示了字母数字和常用的符号。这是最初开始制定的,后面觉得128字符太少了,就用8位二进制来表示,即256个字符,后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。

ASCII的码表太长,这里就不显示出来了,可以从这里找到: 请跳到标准表目录下

Unicode

很明显,ASCII只能代表256个字符。那么对于其他国家的语言文字,却不能标识,例如我们的汉字。为了解决这个问题,出来了Unicode字符集

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符集。目前的Unicode字符分为17组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。在基本多文种平面(英文为 Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要用四位十六进制数(例如U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五位或六位十六进制数了。

在表示一个Unicode的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在js中,会看到以'\u'开头的字符,那表示的是Unicode字符。

毕竟ASCII在计算机的地位还是很高的(谁叫它早问世呢),为了兼容ASCII,Unicode的码值0~127代表的字符,跟ASCII是一样的,因为那是基础ASCII码,被应用的范围已经很广泛了。

比如字母"a"的Unicode编码是0x0061,十进制是97,而"a"的ASCII编码是0x61,十进制也是97。

编码方案

UTF-8

UTF-8是针对Unicode的一种可变长度字符编码。什么是“可变长度”?首先我们先明白,UTF-8问世的初衷。

我们上面知道,Unicode和ASCII存在差异,但是又有一部分是相同的(码值0~127),由于Unicode是16为二进制表示的,所占内存也相应比较大,而且高位多出的0对于ASCII来说是多余的,所以,能用ASCII表示的,就用ASCII表示。

那我们就需要有一个方案,把在Unicode中能表示ASCII的部分(0~127)的,就转化为用ASCII的形式表示该字符,其余码值就继续用Unicode表示。因此用这个方案转化出来的二进制长度是变化的,ASCII的短。这就是UTF-8了,可变长度也就是这么个意思了。

base64

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,这是将二进制数据转化为字符表示编码方案(64种可打印的字符)。它有自己的码表。

主要是用来http环境下传输较长的标识内容,进行base64编码后,可以转化为较短的字符表示。

采用Base64编码具有不可读性,需要解码后才能阅读,所以也有人将其用作简单加密处理。

标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换。

关于这个编码的规则:

  1. 把3个字节变成4个字节。
  2. 每76个字符加一个换行符。
  3. 最后的结束符也要处理。

使用base64编码原理,对字符做base64编码的话(常用于简单的加密),这个过程是:是先将字符转化为对应的ASCII的码值,用二进制表示,然后再做二进制向字符转换的base64编码。

上面规则的第一条说把三个字节变成4个字节,可以用以下文字描述这么一个变化过程。

把三个字节即三个8bit的二进制,视为一个整体,接着从左往右数6位,把这24位数拆成4组,最后每组高位(前面)补两个0,这就形成了4组8bit二进制了,对应4个字符。

那么如果实际要编码的内容不足三个字符或三的倍数个字符的话,仍然按照上面的规则,按顺序把能拆出来的6bit一组先拆出来,直到剩余的位数不足构成6个时,就低位(右边)补零成6位。此时把拆出来的各组再高位补零(即上面规则)。最后,如果对应的字符仍然少于4个,则用'='补全至4个。

从以下例子中加深认识:

我们要对cat进行base64编码

c: ASCII值 99 ,二进制为 0110 0011
a: ASCII值 97 ,二进制为 0110 0001
t: ASCII值 116,二进制为 0111 0100
对这三个字节套用上述规则,以每6位一组,拆成四组:
011000 110110 000101 110100
每组前面加两个00,即:
00011000 00110110 00000101 00110100
对应十进制分别为:24、54、5、52
根据base64的编码表,找出24、54、5、52四个码值分别对应的字符,
编码后结果为:Y2F0

上面是刚好是3或3的倍数的字符的情况,现在举例个不是的例子:

我们要对 c 进行base64编码

c: ASCII值 99 ,二进制为 0110 0011
以每6位一组,拆出了第一组 011000,
剩下11,不足6位,低位补零,变成110000,
到此,顶多能拆出两组,分别为两组前面加两个00,即:
00011000 00110000
对应十进制分别为:24、48
根据base64的编码表,分别对应的字符:Yw
由于转换后的字符数不足4个,用'='来补全,
所以最终结果为: Yw==

码表:

image

所以,Base64编码的使用,初衷且主要目的是对传输二进制数据内容转化为字符传送。但是也会借助其原理用来做字符简单加密处理。 还有一个较常用的使用场景是:在html里img标签上直接使用图片的的base64编码url,这样将图片这种二进制数据进行编码,内容可直接内嵌在页面中,避免不必要的外部资源加载,增大页面加载时间

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions