字符集
一组字符的集合,每个字符都有一个唯一的编号,称为码点。字符集的作用是将字符和码点一一对应起来,形成一个对照表。


ASCII
早期计算机起源于美国,ASCII 字符集是为英语设计的。共收录了 128 个字符,其中包括:可见字符(字母、数字、标点符号)和 控制字符(Tab、回车)。
GBK
ASCII 字符集无法兼容中文字符,为解决中文编码问题,设计出了 GBK 字符集,也被称为国标码,兼容 ASCII 字符集。GBK 共收录了 21886 个字符,包括汉字(部首、构件)和图形符号。
Unicode
为实现各国语言字符的互通,国际上设计出了Unicode 字符集,也被称为万国码,兼容 ASCII 字符集。它包含了3万多个字符。
Base64
由于 ASCII 字符集中包含控制字符,导致并非所有字符都是可见的。为了将二进制数据编码为可见的文本数据,Base64 字符集被提出。它包含 64 个字符,由大小写字母(A-Z、a-z)、数字(0-9)以及两个特殊字符(通常是"+"和"/")组成,再加上一个用于填充的"="字符。
编码
编码 是指将原始数据(如文本、图片)按照不同的 字符集 转换为二进制 数据。反过来,将二进制数据 转换回原始数据的过程被称为解码。

UTF-8
最常见的编码方式,使用了Unicode 字符集进行转换字符,编码输出的二进制长度是1字节~4字节,也就是动态长度。
字符 | 码点 | 二进制 |
---|---|---|
我 |
25105 | 01100010 00010001 |
i | 105 | 01101001 |
你 | 20320 | 01001111 01100000 |
如果我们按照上面的二进制储存到文件中:
我i你 ==> 0110001000010001011010010100111101100000
这就遇到一个问题,如何去解析文件中的二进制串喃?因为UTF-8
的自己是动态字节长度的。
当前字符需要解析多少个字节是未知的,比如:
前8位解析Unicode 字符是 b
前16位解析Unicode 字符是 我
前24位解析Unicode 字符是 错误!!
动态字节长度 是 UTF-8 的特点,也是他必须要解决的问题,所以 UTF-8 编码设置了一套二进制储存规范。
UTF-8编码「二进制」 | |
---|---|
0xxxxxxxx | 1字节 |
110xxxxx 10xxxxxx | 2字节 |
1110xxxx 10xxxxxx 10xxxxxx | 3字节 |
11110xxx 10xxxxxx 10xxxxxx | 4字节 |
字符 | 码点 | 二进制 |
---|---|---|
我 |
25105 | 11100110 10001001 10000001 |
i | 105 | 01101001 |
你 | 20320 | 11100100 10111101 10100000 |
我i你 ==> 11100110100010011000000101101001111001001011110110100000
虽然比原始的二进制串长,但是可以去按照开头的标识去解析二进制串了
UTF-32
前面聊到 UTF-8 的时候,不得不提到 UTF-32 。不过,UTF-32 出现得比 UTF-8 早,但后来却逐渐被淘汰了。
UTF-32 是一种固定字节的编码方式,不论字符在 Unicode 字符集中处于什么码点,都用 4 字节 来记录。这样做的好处是字符表示简单直接,能涵盖所有 Unicode 字符,还避免了像 UTF-8 那样需要识别字节前缀的复杂性。
但它的缺点也很明显,那就是太占空间了。每个字符都用 4 字节,即使是简单的 ASCII 字符也不例外,这在存储和传输大量文本时,会带来很大的资源浪费。
所以,尽管 UTF-32 在某些特定场景下仍有其价值,但整体上,它还是因为效率和资源占用的问题,逐渐被 UTF-8 等编码方式取代了。
Base64
Base64编码 是一种将数据转换为可打印字符的编码方式,常用于文件、字符串等数据的可视化传递。
- 使用 64个字符(包括大小写字母、数字和特殊符号)作为字符集,确保所有字符都是可视的。
解码

- 在 解码 时,以 6位 二进制为一组进行分割。如果最后的二进制数量不足 6位 ,则进行补 0。
- 在 解码 成功时,判断 Base64 长度是否能被 4 整除,如果不足 4 个字符,就进行补 =。
js
// 定义Base64字符集
const base64CharSet = {};
// 初始化Base64字符集,将索引与字符对应
`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=`.split("").forEach((char, index) => base64CharSet[index] = char);
/**
* 将字符串转换为Base64编码
* @param {string} inputStr - 需要转换的字符串
* @returns {string} Base64编码后的字符串
*/
function stringToBase64(inputStr) {
// 用于存储最终的Base64编码结果
let base64String = "";
// 将输入字符串的每个字符转换为对应的ASCII码的二进制表示,并拼接成一个长的二进制字符串
// 注意:每个字符的二进制表示需要补足8位
let binaryString = inputStr.split("").map(char => {
return char.charCodeAt().toString(2).padStart(8, '0');
}).join("");
// 如果二进制字符串的长度不是6的倍数,则在末尾补0,使其长度成为6的倍数
if (binaryString.length % 6 !== 0) {
binaryString += "0".repeat(6 - binaryString.length % 6);
}
// 遍历二进制字符串,每6位一组进行处理
for (let index = 0; index < binaryString.length; index += 6) {
// 提取每6位二进制字符
let sixBitSegment = binaryString.substring(index, index + 6);
// 将6位二进制字符转换为十进制索引
let indexInBase64Set = parseInt(sixBitSegment, 2);
// 根据索引从Base64字符集中获取对应的字符,并添加到结果中
base64String += base64CharSet[indexInBase64Set];
}
// 如果Base64编码结果的长度不是4的倍数,则在末尾补"="字符
if (base64String.length % 4 !== 0) {
base64String += "=".repeat(4 - base64String.length % 4);
}
// 返回最终的Base64编码字符串
return base64String;
}
// 测试代码
console.log(stringToBase64("hello world")); // 输出Base64编码结果