编码与解码

# 编码与解码

参考教程:阮一峰-字符编码笔记:ASCII,Unicode 和 UTF-8 (opens new window)前端数据操作总结 (opens new window)

[TOC]

# 一、字符编码

1B(byte,字节)= 8 bit

# 1.1 ASCII码

ASCII码共规定了128个字符,由7位二进制表示即可。英语用128个字符编码就够了,一个ASCII码就是1B。

'A'.charCodeAt(0) // 65
String.fromCharCode(65)	// A
1
2

# 1.2 Unicode

  • Unicode是一个符号集,可容纳100多万个符号,支持多语言环境。

  • charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。字符串中第一个字符的位置为 0, 第二个字符位置为 1,以此类推。

    • '中'.charCodeAt(0)// 20013
  • 对于英文字母,UTF-8 编码和 ASCII 码是相同的。

# 1.2.1 UTF-8

  • 在互联网上使用最广的一种 Unicode 的实现方式。

  • 最大特点:可变长的编码方式。可使用1-4个字节表示一个符号,根据不同的符号而变化字节长度,可避免因过多的存储浪费导致文件大小变大。

  • 编码规则

    • 于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。
    • 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
  • Web要求URL的查询字符串采用UTF-8编码,对于一些特殊字符或者中文等,会编码成多个字节,变成%加相应16进制码的形式。比如:汉字 中 将会被编码为%E4%B8%AD。为此JS提供了encodeURIComponent与decodeURIComponent方法。

    encodeURIComponent('中') // "%E4%B8%AD"

    故可借此来实现UTF-8的编码与解码。

    编码:

function encodeUtf8(text) {
    const code = encodeURIComponent(text);
    const bytes = [];
    for (var i = 0; i < code.length; i++) {
        const c = code.charAt(i);
        if (c === '%') {
            const hex = code.charAt(i + 1) + code.charAt(i + 2);
            const hexVal = parseInt(hex, 16);
            bytes.push(hexVal);
            i += 2;
        } else bytes.push(c.charCodeAt(0));
    }
    return bytes;
}

encodeUtf8('中') // [228, 184, 173]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

​ 解码:

function decodeUtf8(bytes) {
    var encoded = "";
    for (var i = 0; i < bytes.length; i++) {
        encoded += '%' + bytes[i].toString(16);
    }
    return decodeURIComponent(encoded);
}

decodeUtf8([228, 184, 173])	// '中'
1
2
3
4
5
6
7
8
9

# 1.2.2 UTF-16、UTF-32

基本不用。UTF-16的字符用两个字节或四个字节表示,UTF-32的字符用四个字节表示。

# 1.3 GB类

  • 中国大陆几乎所有的中文系统和国际化的软件都支持GB2312。GB2312是简体中文常见的编码方式,使用两个字节表示一个汉字,所以最多可以表示2的16次方65536个符号。

  • GB2312基本满足了计算机处理简体汉字的需求,所收录的汉字覆盖了99.75%的使用频率,但对于罕见字和繁体字,GB2312就不能处理了。因此发明了后来的GBK和GB18030。

  • GB18030 > GBK > GB2312

  • 计算机操作系统中的编码:

    • Windows下中文的默认编码是GBK(GB2312)
    • Linux下中文的默认编码是UTF-8

# 二、URI编码与解码

# 2.1 encodeURIComponent()编码

对统一资源标识符(URI)的组成部分进行编码的方法。它使用一到四个转义序列来表示字符串中的每个字符的UTF-8编码(只有由两个Unicode代理区字符组成的字符才用四个转义字符编码)。

  • 不转义的字符

    A-Z a-z 0-9 - _ . ! ~ * ' ( )

  • 保留字符会转成%XX形式(#不是保留字符)

    encodeURIComponent(';,/?:@&=+$#')	// "%3B%2C%2F%3F%3A%40%26%3D%2B%24%23"
    
    1

# 2.2 decodeURIComponent()解码

用于解码由 encodeURIComponent (opens new window) 方法或者其它类似方法编码的部分统一资源标识符(URI)。

当该方法使用不当时,将会抛出一个URIError (opens new window)(“格式错误的URI序列”)异常。

encodeURI、decodeURI与上两者的区别在于,不会对任何保留字符和#进行编码处理。

# 三、Base64的编码与解码

参考教程:Base64的原理、实现及应用 (opens new window)JS的Base64编码解码 (opens new window)

Base64编码是基于64个字符A-Z,a-z,0-9,+,/的编码方式,因为2的6次方正好为64,所以就用6bit(六位二进制)就可以表示出64个字符,用来将二进制数据转成文本数据,可确保内容在各个网关间无措传输。

# 2.1 Base64编码

  • btoa():从一个字符串或者二进制数据编码一个Base64字符串。(反着记,a-to-base64)
    • 仅支持ASCII,btoa('中')会报错。对此,可以对整个字符串进行转义(如使用encodeURIComponent进行UTF-8转义)然后再btoa编码。
  • 标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的保留字符和#变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。

综上所述,字符串转Base64字符串如下:

function encode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
    // match是匹配的字符,p1是([0-9A-F]{2})
    function toSolidBytes(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}
1
2
3
4
5
6
7

# 2.2 Base64解码

  • atob():解码一个Base64字符串。
function decode(str) {
    return decodeURIComponent(atob(str).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}
1
2
3
4
5

# 三、ArrayBuffer

应用场景:canvas 图像处理、WebGL 与显卡通信、文件操作、Ajax响应。

# 3.1 ArrayBuffer转string

let uint8Array = new Uint8Array(arrayBuffer)
let stringData = String.fromCharCode.apply(null, uint8Array)
let postedString = decodeURIComponent(escape(stringData))	// 可避免中文乱码
1
2
3