Unicode编码解明

简介

本文简述了Unicode编码解码相关知识。假设读者已经对此有一定的基本了解

从Base64开始

为了更好说明变长字符编码,我们从Base64编码开始。因为Base64是每个字节进行编码

编码如下字符串:

1
你好, 世界!

得到如下编码:

1
5L2g5aW9LCDkuJbnlYwh

将这段编码放入浏览器控制台中进行不进行任何处理的解密

1
atob('5L2g5aW9LCDkuJbnlYwh');

得到如下结果:

1
你好, 世界!

可以阴影约约看到逗号和感叹号已经被解释出来,而中文没有被正确处理。因为英文的逗号和感叹号在128位ascii表中。而其他的中文字符就无法正确翻译了。

为了能正确翻译中文。我们需要对数据进行一些特殊处理。即实现utf8的编码规范来将二进制转换成一个unicode码,使其能对应上一个具体的字符。(unicode表是一个巨大的map, 每个数字都能对应一个具体的字符, 至于字符的具体渲染由系统提供的字体显示)

atob
atob 是一个很基础的base64转二进制的方法。他只会单独的去处理每个字节而不管其具体的编码实现

UTF8解码

我们先处理第一个字符ä
很明显。这是因为浏览器错误的处理了这个字节的翻译。我们需要将其转换成二进制

1
2
3
4
'ä'.charCodeAt(0); // 228

// 将其转换成二进制
(228).toString(2); // 11100100

我们查看一下[utf8的编码规范](https://zh.wikipedia.org/wiki/UTF-8)。首字符前n个1表示由n个字节组成.以0表示收尾与分割。因此我们提取出其描述的二进制位为1110表示这个字由3个字节组成。那么我们继续提取接下来的2个字符

1
2
'½'.charCodeAt(0).toString(2); // 10111101
' '.charCodeAt(0).toString(2); // 10100000

其中最前的二位10是描述位,是无效的。我们将这三个字节的有效的二进制位提取出来可得: 0100 111101 100000。将其转换成16进制

1
(0b0100111101100000).toString(16); // 4f60

简单验证一下结果。直接在控制台输入:

1
'\u4f60' // 你

我们成功提取出了第一个中文字符。那么剩下的中文字符也很简单了

而编码就是其逆过程

UTF8编码

1
2
3
4
5
6
7
8
// 将 你好, 世界! 编码成16进制字符
const str = '你好, 世界!'

str
.split("")
.map(char => char.charCodeAt(0).toString(16))
.map(hex => "\\u" + hex.padStart(4, "0"))
.join(""); // \u4f60\u597d\u002c\u0020\u4e16\u754c\u0021

直接将输出的字符串复制到控制台可以看到自动转换出的中文

\u表示的是只后面跟的是一个unicode。长度为4字节

文章目录
  1. 1. 简介
  2. 2. 从Base64开始
  3. 3. UTF8解码
  4. 4. UTF8编码