字符和编码漫谈

194 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

什么叫字符

组成数据和信息的文字、字母、数字以及各种符号的统称。-- 这是网上抄来的

比如说:

  • 一个汉字就是一个字符
  • 一个英文字母也是一个字符
  • 一个数字也是一个字符
  • 一个标点符号也是一个字符
  • 任意语言任意符号都是一个字符

什么叫字符集

字符集就是一个集合,这个集合把字符按照编号进行收编。

那么是不是一个字符集就能包含所有的字符呢?

显然是不对的。

比如说 ASCII 字符,只包含了英文字母和数字,和一些常用的电脑符号,比如说回车,空格等。

现在最大的字符集是什么:Unicode 字符集,几乎包含了所有出现的字符,甚至emoji表情也被认为是字符,被包含了进去。

字符编码

字符集只是一个集合,告知了每一个字符的编号,但是,他并没有告知,如何在计算机中来存储这个编号。

例如,在Unicode字符集中,汉字的编号是U+7801

此时,如果你要存储这个字,你完全可以存一个整数(int):7801

那么,只要读取这个 int 的程序或者系统,他也跟你一样,明白 7801这个整数就是字的话,那么就是可以的。

不过,我们只是将这个字进行了编码,这是不够的,我们要考虑这个字符集中,所有的字符该怎么存的问题。

至少:

  • 不能重复,也就是不能说两个字符,都存 7801
  • 尽量占少一点字节,你不能一个字符,存个几十个字节,那么一篇小说,就炸了

ASCII 是如何编码的

当我们说ASCII的时候,既是再说这个字符集,也是在说这个编码,因为早期,这两个概念并没有分得太开。

这个编码很简单,因为他就包含127个字符(不算扩展), 用一个字节来存储就行了,每一个数字代表一个字符。

例如:

  • A --> 0x41
  • B --> 0x42

等等

对Unicode字符集进行编码

这是一个巨大的工程,其实。

首先,为了兼容ASCII, 必须保证,原先用ASCII编码存储的内容,这个新的编码照样能读出来。

比如说:

"ABCDEFG" 这一段文字,如果用ASCII编码保存:

0x41 0x42 0x43 0x44 0x45 0x46 0x47

就是7个字节,每个字节的内容如上。

现在,用新的编码,来读取上述七个字节,要依然能解析出:

"ABCDEFG" 这一段文字。

也就是说,对于ASCII码部分,新编码要完全一致。

但是,ASCII码只用了一个字节,不可能包含所有的字符。

于是,一种解决方案就提出来:

变长码

就是说,对于字符集里的字符来说,编码储存之后的字节数,并不一定要相等。

有的用一个字节,有的用两个字节,等等。

UTF-8 这种编码方式

此时此刻,我们来讲一讲现在应用最多的Unicode编码方式:UTF-8。

  • 首先,他是一个变长码。
  • 第二,兼容ASCII。

举个例子:

英文A --> Unicode编号:U+0041 --> UTF-8编码: 0x41 占一个字节,并且与ASCII一致。

汉字 --> Unicode编号:U+7801 --> UTF-8编码: 0xE7 0xA0 0x81 占三个字节。

从这里可以更加清晰的看出编号编码的概念。

UTF-8 的编码过程

所谓编码过程,就是你知道一个字符的编号之后,该如何把这个编号,转化成具体的字节的过程。

我们用 这个汉字来说明这个过程:

  • 首先找到的编号是U+7801
  • 然后来看一下这个图(来自维基百科)

image.png

这个图的含义是,如果编号在

U+0000 --> u+007F 之间,那么用第一行的逻辑。
U+0080 --> u+07FF 之间,那么用第二行的逻辑。
U+0800 --> u+FFFF 之间,那么用第三行的逻辑。
U+10000 --> u+10FFFF 之间,那么用第四行的逻辑。

很明显,我们的U+7801属于第三行。

那么第三行的逻辑是什么?

首先,第三行,就已经固定了三个字节了,并且三个字节的高位,已经写死了

第1个字节:1110xxxx
第2个字节:10xxxxxx
第3个字节:10xxxxxx

剩下就是,如何将7801填充进上面的xxxxxxxxxx里面去。

也简单:

我们来看一下x的个数,正好是16个,也就是两个字节。

我们将 7801 看做 0x78 0x01 ,也正好是两个字节

直接将这个两个字节的内容,按顺序放入上面的xxxx里面去:

第1个字节:1110xxxx -> 1110 0111 -> 0xE7
第2个字节:10xxxxxx -> 10 1000 00 -> 0xA0
第3个字节:10xxxxxx -> 10 00 0001 -> 0x81

于是,->0xE7 0xA0 0x81

so easy ~~~