如果对 UTF-8 的编码方式还不熟悉,请先阅读阮一峰老师的科普,了解一下什么是UTF-8编码。
简单来说,我们要实现一个函数,他的作用就是完成输入字符的UTF-8编码,下面过一下实现思路。
utf8Encode实现思路
如果我们要实现一个 UTF-8Encoding函数,那么我们需要
- 将接收到的字符转化为二进制
- 将二进制的数据按照表格中的规格填入进去,不够的位补 0 来占位
以一个简单的字符为例子我们来实现一下UTF-8Encode的大概实现流程
// 首先定义一个我们要进行utf8encode的字符
var code = "陈"
// 找到他的二进制查看范围
var binrayCode = code.codePointAt(0).toString(2) // "1001011001001000" 总共16位,通过上述表格bit数为16命中最下面的规格
/*
接下来我们要从后往前将他的二进制位数按照下面的X的格式填补上去,不够补0
1110 XXXX
10XX XXXX
10XX XXXX
我们的binrayCode为1001 011001 001000,按照格式填写后如下
1110 1001
1001 1001
1000 1000
由于是二进制,在JavaScript中我们要加一个0b加以标识,所以我们“陈”的utf-8编码为
[0b11101001, 0b10011001, 0b10001000]
*/
可以看到,实现的重点以及难点主要是我们如何能够把对应的二进制位数按照格式填充进相应的规范里。这里我们需要使用到JavaScript位运算符,如果你对JavaScript位运算符还不了解的话,我先带大家简单的学习了解一下
位运算符
我们这里介绍之后需要用到的几个位运算符|
、&
和>>
&
位与运算符的运算规则是:两个数的二进制表示,相同位数如果均为1,则为1;如果其中一个是0,则为0
举个例子:
var num1 = 0b1001
var num2 = 0b0011
/*
其中0b是二进制数的表示
我们把相同的位一一对应,那么按照上述规则,num1 & num2 的运算如下
0b 1001
0b 0011
-------
0b 0001
所以 num1 & num2 结果为 0b0001
*/
通过&
的特性,我们可以取出二进制数字中特定的位数,比如说我们想要取出一个二进制数字的后5位,那么我们只需要计算11111 & 二进制数
。这样由于超出11111
的部分前面全为0,所以永远都不会是1。只有在11111
的部分会根据二进制的对应位数进行计算。同样的举个例子:
// 我们希望取后4位
var num1 = 0b1111
var num2 = 0b100101001010
/*
num1 & num2
0b 1111
0b 100101001010
未填满的位置自动补0,则得到
0b 000000001111
0b 100101001010
---------------
0b 000000001010
所以 num1 & num2 结果为 0b 000000001010 也就是 0b1010
可以看到,除了1111,之前的位数由于都是0,所以不管num2对应的位数是什么,在&操作后都只会返回0。之后的有1则是1,有0则是0。我们自然就取到最后四位了
*/
|
位或运算符的运算规则是:两个数的二进制表示,相同位数如果有1,则为1;如果都是0,则为0
举个例子:
var num1 = 0b1001
var num2 = 0b0011
/*
其中0b是二进制数的表示
我们把相同的位一一对应,那么按照上述规则,num1 | num2 的运算如下
0b 1001
0b 0011
-------
0b 1011
所以 num1 | num2 结果为 0b1011
*/
通过或的特性,我们可以做一些事情:比如说按照10XXX的格式填充二进制数字,那么我们只需要根据上述的&
操作进行111 & 二进制数字
,取出对应的后三位。再把得到的结果 | 10XXX
就可以按照格式填充对应的数字了
>>
通过刚才介绍的&
与|
操作符,我们已经可以自由的截取二进制数字的最后几位了。那么如果我们希望截取中间的几位应该如何操作呢?如果可以把希望截取的最后一位移到最后,我们的问题就可以解决了。而移动的关键,就是>>
移位操作符,下面简单的介绍一下它的使用方法
var num1 = 0b100110001
num1 >> 2 // 整体向右移动2位,多余的空间用0占位
/*
那么num1就会这样进行变化:
100110001
1001100【01被移出】
001001100
最终倒数第二位变成了最后一位
*/
那么num1的长度9,现在我们希望截取他的2-5位,应该如何操作呢?
我们只需要首先将 num1 >> 4
来右移4位 ,让最后一位变成5,然后通过& 1111
截取对应4位(2-5)就可以了。
知道了以上的几个操作概念,我们就可以编写utf8Encode函数了
代码实现
回顾一下utf8Encode的思路:在根据表格规格填进去的步骤,我们需要以下几步来操作
- 通过
&
操作从后往前依次取出 x 位 - 通过
|
操作将取出的位数按照格式填进去 - 将所有操作的数拼接在一起
结合上面所说的位操作,如果我们希望一段二进制的编码满足这种格式进行填充
function utf8Encode(str) {
// utf8Arr
utf8arr = []
// 转化为码点
const code = str.codePointAt()
if (code <= 0x007f) {
// length: 7
// 0XXXX XXXX
// 取7位
let get7 = 0b1111111 & code
// 写7位
utf8arr.push(0b00000000 | get7)
} else if (code >= 0x0080 && code <= 0x07ff) {
// length: 5 + 6
// 110X XXXX
// 10XX XXXX
// 取5位
let get5 = 0b11111 & (code >> 6)
utf8arr.push(0b11000000 | get5)
// 取6位
let get6 = 0b111111 & code
utf8arr.push(0b10000000 | get6)
} else if (code >= 0x0800 && code <= 0xffff) {
// length: 4 + 6 + 6
// 1110 XXXX
// 10XX XXXX
// 10XX XXXX
// 取4位
let get4 = 0b1111 & (code >> 12)
utf8arr.push(0b11100000 | get4)
// 取6位
let get6 = 0b111111 & (code >> 6)
utf8arr.push(0b10000000 | get6)
// 取6位
let get62 = 0b111111 & code
utf8arr.push(0b10000000 | get62)
}
return utf8arr
}
console.log(utf8Encode("陈"))
总结
在实现一个utf8Encoding
函数的过程中,我们学习到了位运算符的一些使用,也了解了utf8编码的实现过程,再来回顾一下几个知识点。
- 使用
&
操作取对应位数的字符 - 使用
|
操作按规范填入位数 - 通过移位来解决多段截取的问题
- utf8的编码过程