玩转代码|JS实现中文字符串对utf-8的Base64编码的方法

目录

UTF-8 字符串编解码

解决方法

解析

utf8_to_b64

b64_to_utf8

弃用 unescape 和 escape 方法

原由

解决方法

Node.js 下的 Base64 编解码


Base64 编解码

Base64是一种使用64基的位置计数法。它使用2的最大次方来代表仅可打印的ASCII 字符。这使它可用来作为电子邮件的传输编码。在Base64中的变量使用字符A-Z、a-z和0-9 ,这样共有62个字符,用来作为开始的64个数字,最后两个用来作为数字的符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后binhex的版本使用不同的64字符集来代表6个二进制数字,但是它们不叫Base64。

其实在 JavaScript 中,原生就有两个函数被分别用来处理解码和编码 base64 字符串:

  • btoa(): 从二进制数据的 “字符串” 创建一个 Base64 编码的 ASCII 字符串(“btoa” 其实是 “二进制转 ASCII” 的意思)。
  • atob(): 解码 Base64 编码的字符串(“atob” 其实是 “ASCII 到二进制” 的意思)。

可以通过 window.atob(string)window.btoa(base64string) 的方式调用,非常方便。

UTF-8 字符串编解码

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。

如上面的描述所言,btoa 和 atob 都只支持 ASCII 字符,并不支持 Unicode 字符。在大多数浏览器中对 Unicode 字符串调用将会报 Character Out Of Range 错误,因为字符超出了 ASCII 的范围。

解决方法

我们可以将字符串转义过后再进行编码,解码时将解码结果重新转义为 Unicode 字符串。

function utf8_to_b64(str) {
  return btoa(unescape(encodeURIComponent(str)));
}

function b64_to_utf8(str) {
  return decodeURIComponent(escape(atob(str)));
}

// 用例:
utf8_to_b64("测试"); // "5rWL6K+V"
b64_to_utf8("5rWL6K+V"); // "测试"

解析

这中间看起来比较神奇,发生了个什么事呢?

主要呢就是利用了 encodeURIComponentdecodeURIComponent 会把接受到的字符串参数当作 UTF-8 字符串来进行处理。

utf8_to_b64

先看 utf8_to_b64 方法。

这里由于 encodeURIComponent 方法接受的是 UTF-8 字符串,可以先用 encodeURIComponent 方法将 UTF-8 字符串转成了形如 %XX%XX 的十六进制符号。然后使用 unescape 方法将十六进制翻译为了 ASCII 中对应的内容,这样就变成了 btoa 方法能够接受的 ASCII 字符串。最后直接使用 btoa 方法编码为 Base64 字符串。

encodeURIComponent("测试"); // "%E6%B5%8B%E8%AF%95"
unescape("%E6%B5%8B%E8%AF%95"); // "æµ\x8Bè¯\x95"
btoa("æµ\x8Bè¯\x95"); // "5rWL6K+V"

总的来说就是一个将 UTF-8 字符串转为 ASCII 字符串再编码的一个过程。

b64_to_utf8

再看 b64_to_utf8 方法。

其实就是反过来,先将 Base64 字符串通过 atob 方法解码为 ASCII 字符串,然后通过 escape 方法将 ASCII 字符串转为十六进制符号,最后将十六进制符号通过 decodeURIComponent 方法解析为 UTF-8。

atob("5rWL6K+V"); // "æµ\x8Bè¯\x95"
escape("æµ\x8Bè¯\x95"); // "%E6%B5%8B%E8%AF%95"
decodeURIComponent("%E6%B5%8B%E8%AF%95"); // "测试"

弃用 unescape 和 escape 方法

原由

该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。

可以看到 unescape 和 escape 方法已经被标记为废弃,并推荐使用 decodeURI 或 decodeURIComponent 替代 unescape,推荐使用 encodeURI 或 encodeURIComponent 替代 escape

根据百分号编码 - 维基百科中的内容可以知道,escape 在处理 0xff 之外字符的时候,是直接使用字符的 unicode 在前面加上一个 「% u」,而 encodeURI 则是先进行 UTF-8,再在 UTF-8 的每个字节码前加上一个 「%」

2005 年 1 月发布的 RFC 3986,建议所有新的 URI 必须对未保留字符不加以百分号编码;其它字符建议先转换为 UTF-8 字节序列,然后对其字节值使用百分号编码。此前的 URI 不受此标准的影响。
有一些不符合标准的把 Unicode 字符在 URI 中表示为: %uxxxx, 其中 xxxx 是用 4 个十六进制数字表示的 Unicode 的码位值。任何 RFC 都没有这样的字符表示方法,并且已经被 W3C 拒绝 (页面存档备份,存于互联网档案馆)。第三版的 ECMA-262 仍然包含函数 escape(string) 使用这种语法,但也有函数 encodeURI(uri) 转换字符到 UTF-8 字节序列并用百分号编码每个字节。

所以 escape 是对百分号编码的非标准实现,所以被废弃实属正常。

解决方法

虽说 escape 是对百分号编码的非标准实现,但是在上面的方法中我们实际上利用了 escape 的这种特性,这边提供了不使用 unescape 和 escape 方法后的实现。

function utf8_to_b64(str) {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
      return String.fromCharCode("0x" + p1);
    })
  );
}

function b64_to_utf8(str) {
  return decodeURIComponent(
    atob(str)
      .split("")
      .map(function (c) {
        return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
      })
      .join("")
  );
}

// 用例:
utf8_to_b64("测试"); // "5rWL6K+V"
b64_to_utf8("5rWL6K+V"); // "测试"

Node.js 下的 Base64 编解码

在 Node.js 中使用上面的方法,你可能会发现,btoa 和 atob 方法,由于只支持 ASCII 方法也已经被标记为废弃了,那么在 Node.js 中用什么方法呢?

Node.js 中提供了一个更加简便的方法,那就是利用 Buffer,除了支持字符串,也支持其他数据。

function utf8_to_b64(str) {
  return Buffer.from(str).toString("base64");
}

function b64_to_utf8(str) {
  return Buffer.from(str, "base64").toString("utf8");
}

猜你喜欢

转载自blog.csdn.net/qq_22903531/article/details/131934492