首先了解几个简单的知识点:
- unicode 是一个字符集,它规定一个字符对应哪一个二进制
- utf-8 是 unicode字符集 的编码方式,即表现形式之一
了解一下 UTF-8
UTF-8 是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
编码规则:
- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
- 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
编码规则表,字母x表示可用编码的位
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 |
根据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
//01100001
const str = '被讨厌的勇气';
//str2bin(str);
console.log(str2bin(str, true));
/**
* 字符串转 utf-8 二进制
*
* UTF-8 是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
* 两条规则:
* 1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
* 2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
* @param str
*/
function str2bin(str, byByte = false) {
let str_arr = new Array(4);
let unicode_num, bin_arr = new Array();
for (let i = 0; i < str.length; i++) {
str_arr.fill('');
unicode_num = str.charCodeAt(i);
// 十六进制范围:00000000 ~ 0000007F
// 二进制范围:0 ~ 2^7-1
// 格式:0xxxxxxx
if (unicode_num < 128) {
str_arr[0] = pad_zero(unicode_num.toString(2), 8);
}
// 00000080 ~ 000007FF
// 2^7 ~ 2^11-1
// 格式:110xxxxx 10xxxxxx
else if (unicode_num >= 128 && unicode_num < 2048) {
// 第一字节 110xxxxx
//1. 右移6位, 即去掉右边6位
//2. 填充到 110xxxxx, 即设置7位和6位为1, 1100000 = 2^7 + 2^6 = 128 + 64 = 192
str_arr[0] = ((unicode_num >> 6) | 192).toString(2);
// 第二字节 10xxxxxx
//1. 先取后6位, 63 = 64 - 1 = 2^6 - 1 = 1000000 - 1 = 111111
//2. 填充到 10xxxxxx 中, 1000000 = 2^7 = 128, a | 128 即设置a的7位为1,其他保持不变
str_arr[1] = ((unicode_num & 63) | 128).toString(2);
}
// 00000800 ~ 0000FFFF
// 2^11 ~ 2^16-1
// 格式:1110xxxx 10xxxxxx 10xxxxxx
else if (unicode_num >= 128 && unicode_num < 65536) {
// 第一字节
// 1.右移12位
// 2.填充到 1110xxxx, 11100000 = 2^7 + 2^6 + 2^5 = 224
str_arr[0] = ((unicode_num >> 12) | 224).toString(2);
// 第二字节 - 取中间6位
// 1.右移6位
// 2.取后6位
// 3.填充到 10xxxxxx
str_arr[1] = (((unicode_num >> 6) & 63) | 128).toString(2);
// 第三字节 - 取后6位
str_arr[2] = ((unicode_num & 63) | 128).toString(2);
}
// 00010000 ~ 0010FFFF
// 2^16 ~ (2^17 + 2^16 - 1)
// 65536 ~ 196607
// 格式:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
else {
// 第一字节
// 1.右移18位
// 2.填充到 11110xxx, 11110000 = 2^7 + 2^6 + 2^5 + 2^4 = 240
str_arr[0] = ((unicode_num >> 18) | 240).toString(2);
str_arr[1] = (((unicode_num >> 12) & 63) | 128).toString(2);
str_arr[2] = (((unicode_num >> 6) & 63) | 128).toString(2);
str_arr[3] = ((unicode_num & 63) | 128).toString(2);
}
// 按字节返回 或 按字符返回
if (byByte) {
bin_arr = bin_arr.concat(str_arr.filter(item => {
return item.length > 0;
}));
} else {
bin_arr.push(str_arr.join(''));
}
}
return bin_arr;
}
/**
* 填充0
* @param input
* @param pad_len
* @param pad_type 0:左填充 1:右填充
* @returns {number|string|number}
*/
function pad_zero(input, pad_len, pad_type = 0) {
if (typeof input == 'number') {
input += '';
}
let str_len = input.length;
if (str_len >= pad_len) {
return input;
}
for (let i = 0; i < pad_len - str_len; i++) {
if (pad_type) {
input += '0';
} else {
input = '0' + input;
}
}
return input;
}
网友评论