在我学习 JavaScript 的时候, 获取字符串的长度的时候,大多数能够获取到正确的字符串长度,但是有些字符获取的长度却大不相同,到底是为什么会导致这样的发生。
在早初的计算机,所有的信息都是靠二进制存储在计算机中的,美国制定了一套字符编码,使得英语字符与二进制之间产生一个映射。每一个二进制位都是由 0 和 1 两种组成的,八位(也就是八个0或者1组成的)被称为是一个字节,八位最多可以组合成 256 中状态。(不懂转换的可以使用转换工具 进制转换)。
这种编码方式称为 ASCII 码。 ASCII 一共规定了 128 个字符的编码,其中还包括 空格是32(二进制 00100000),大写字母 A 是 65(二进制是 01000001)。128个字符包含了英文字母以及32个不能打印出来的控制符号。
数字 32–126 打印字符。
0–31 分配给了控制字符,不可以打印。
十进制 | 字符 | 十进制 | 字符 | |
---|---|---|---|---|
0 | 空 | 16 | 数据链路转意 | |
1 | 头标开始 | 17 | 设备控制 1 | |
2 | 正文开始 | 18 | 设备控制 2 | |
3 | 正文结束 | 19 | 设备控制 3 | |
4 | 传输结束 | 20 | 设备控制 4 | |
5 | 查询 | 21 | 反确认 | |
6 | 确认 | 22 | 同步空闲 | |
7 | 震铃 | 23 | 传输块结束 | |
8 | backspace | 24 | 取消 | |
9 | 水平制表符 | 25 | 媒体结束 | |
10 | 换行/新行 | 26 | 替换 | |
11 | 竖直制表符 | 27 | 转意 | |
12 | 换页/新页 | 28 | 文件分隔符 | |
13 | 回车 | 29 | 组分隔符 | |
14 | 移出 | 30 | 记录分隔符 | |
15 | 移入 | 31 | 单元分隔符 |
除了上述的 128 个字符之外,ASCII 还剩下许多遗留的符号位。
而且对于英语来说,128 个符号编码就够了,但是对于其他语言来说,每个国家都有自己的语言,然后其他国家就利用这剩下的码位,编写自己国家的符号编码,比如在法语中,é(这种字符上带注意就是法语)的编码位 130 (二进制10000010),但是 编码位 130 在希伯来中却代表了字母 ג,所以每个国家都来都来利用 128~255 这一段来编码,对于0~127符号是固定的。
因为不同国家重复的使用一块地方,在转码的时候容易造成乱码。并且 对于我们国家使用汉语的来说,哪怕文字都多达10万左右。用一个字节8位来存储是完全不够的,那么怎么办呢?很简单,一个字符由 256 种存法,那一个字节不够存,我用两个,三字,甚至四个字节来存,不就可以了嘛。
Unicode的诞生。
Unicode 就是用来解决这种的。总有人会意识到应该提出一种编码方式,用来收集所有国家的字符。它用数字记录了所有国家的符号对应的数字。我们不需要关心如何规定的,只需要知道每个字符都对应一个不会重复的二进制数。
我们已经知道了 Unicode 为了解决编码存储范围小,所以采用更大的字节来存储字符。那么Unicode 又怎么会知道自己存储的是需要几个字节的字符呢。先不说Unicode 有多大,我们就假设Unicode 是一个字典,最后一页的最后一位是代码占用四个字节。最简单的莫过于直接规定所有的字符存储都为 4 个字节, 那么所有的字符统统在范围内,只需要前面补 0 即可。这样固然简单,但这也太浪费空间了吧,我要是使用 ASCII 前几位,明明是占一个字节,硬生生补 0 成为 4 个字节,这样合理吗?
因为上文原因,诞生了一种种的 Unicode 的实现方式。
UTF-8 (是 Unicode 的实现方式之一)。
互联网的普及,强烈要求出现统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。 还有UTF-16,UTF-32, 毫无疑问,都是 Unicode 的实现方式。 UTF-32 就是像上面的实现一样,使用定长的4个字节,看起来比较方便,但是却不如 UTF-8 和 UTF-16 .
而 UTF-8 的最大的一个特点就是使用一种 与 UFT-16定长不同的, 变长的编码方式。它是使用 1~4个字节来表示的,可以根据字符来变化不同字节长度。
UTF-8 的 编码规则:
1. 对于单字节来说,也就是 符合 ASCII 编码方式的八位,字节的第一位被设置为 0,后面的 7 位对应这个字符的 Unicode 码点。 英文中的 0~127(二进制 01111111, 十六进制 0000 007F)。
2. 对于需要 N 个字节来表示的字符 (N > 1) 来说,第N个字节(右面第一个位第一个字节算起)的前 N 位都是 1,第 n + 1 位 设置为 0, 然后 后面(从左往右数)的字节前两位一律设为10。剩下的二进制位用这个字符的 Unicode 码点来填充。
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 以 严 的 Unicode 是 4E25 (
100111000100101
), 可以发现它是属于 第三行的 1110xxxx 10xxxxxx 10xxxxxx 表示。因此 严 需要三个字节来表示,我们用100111000100101 来填充
1110xxxx 10xxxxxx 10xxxxxx,从右向左来填充, 第一个字节的xxxxxx,我们用100101来填充,第二个字节的xxxxxx我们用111000填充,第三个字节的xxxx我们用 100来填充,多处的位补 0.
所以我们得到了 Unicode 4E25的 UFT-8编码
11100100 10111000 10100101, 转为十六进制就是 E4B8A5。我们也可以通过网上一些工具来实现 UTF-8 到 Unicode 的实现。注意:我说的是实现,并不是转换,因为 UTF-8 只能算是Unicode的一种实现形式,二者并不是对立的。
像java 和 c 等语言是使用的 UTF-8 语言。 JavaScript 使用了UTF-16格式来对于字符串编码。下一篇我们会着重讲解一下JavaScript中的UTF-16。