JS字符串转GBK编码超精简实现详解

作者:异步漫步时间:2026-10-25 17:30:02浏览:133

本文详解如何在JavaScript中将字符串转换为GBK编码。针对TextEncoder无法指定字集的问题,提出通过遍历GBK字符范围构建映射表的方法。该方案仅需几十行代码和几百字节体积,无需引入iconv等重型第三方库,即可在浏览器端高效完成GBK编码转换,并处理了ASCII字符及特殊符号如欧元符号的情况。

前言

在 JavaScript 中将 GBK 编码的字节流还原为字符串十分便捷,直接调用 TextDecoder 即可完成解码操作。

const gbkBuf = new Uint8Array([196, 227,   186, 195,   49, 50, 51])
new TextDecoder('gbk').decode(gbkBuf)   // "你好123"

然而,逆向过程即字符串转 GBK 编码则较为复杂,因为 TextEncoder 默认仅支持将字符串转换为 UTF-8 编码的二进制数据,无法直接指定 GBK 字集。

目前业界主流方案多依赖第三方编码库(如 iconv)。这类库通常包含庞大的字集数据,即便精简版如 iconv-lite 体积也达数百 kB,在浏览器环境中显得过于臃肿。我们的目标是仅用几百字节即可实现该功能。

遍历

经查阅,GBK 字符集仅包含两万余个字符,最直观的方法是「暴力穷举」。利用 TextDecoder 可遍历生成每个 GBK 编码对应的 JavaScript 字符,后续编码过程即转化为简单的查表操作。

实际上,GBK 的编码范围具有明确的规律性:

前言

https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding

因此,只需在预定义的范围内进行遍历,即便增加十几行代码以提升性能,也是值得投入的。

const ranges = [
  [0xA1, 0xA9,  0xA1, 0xFE],
  [0xB0, 0xF7,  0xA1, 0xFE],
  [0x81, 0xA0,  0x40, 0xFE],
  [0xAA, 0xFE,  0x40, 0xA0],
  [0xA8, 0xA9,  0x40, 0xA0],
  [0xAA, 0xAF,  0xA1, 0xFE],
  [0xF8, 0xFE,  0xA1, 0xFE],
  [0xA1, 0xA7,  0x40, 0xA0],
]
const codes = new Uint16Array(23940)
let i = 0

for (const [b1Begin, b1End, b2Begin, b2End] of ranges) {
  for (let b2 = b2Begin; b2 <= b2End; b2++) {
    if (b2 !== 0x7F) {
      for (let b1 = b1Begin; b1 <= b1End; b1++) {
        codes[i++] = b2 << 8 | b1
      }
    }
  }
}
const str = new TextDecoder('gbk').decode(codes)

// 编码表
const table = new Uint16Array(65536)
for (let i = 0; i < str.length; i++) {
  table[str.charCodeAt(i)] = codes[i]
}

若每处理一个 GBK 编码就调用一次 TextDecoder,效率将极低。为此,我们将所有 GBK 编码值集中存入 codes 数组,最后仅调用一次 TextDecoder 进行批量转换。

该初始化过程耗时仅 1ms 至 2ms,开销极低。

查表

建立映射表后,编码时直接查表即可:

function stringToGbk(str) {
  const buf = new Uint16Array(str.length)
  for (let i = 0; i < str.length; i++) {
    const code = str.charCodeAt(i)
    buf[i] = table[code]
  }
  return new Uint8Array(buf.buffer)
}

stringToGbk('你好')   // [196, 227,   186, 195]

输出结果与文章开头演示一致。

上述逻辑未涵盖 ASCII 字符范围,若传入包含数字的字符串(如「你好123」)则会出现问题。由于 GBK 中 ASCII 部分采用单字节存储,编码逻辑需相应调整:

function stringToGbk(str) {
  const buf = new Uint8Array(str.length * 2)
  let n = 0

  for (let i = 0; i < str.length; i++) {
    const code = str.charCodeAt(i)
    if (code < 0x80) {
      buf[n++] = code
    } else {
      const gbk = table[code]
      buf[n++] = gbk & 0xFF
      buf[n++] = gbk >> 8
    }
  }
  return buf.subarray(0, n)
}

stringToGbk('你好123')    // [196, 227,   186, 195,   49, 50, 51]

输出结果与文章开头演示一致。

出于性能考量,此处使用 Uint8Array 替代 Array。鉴于 Uint8Array 长度固定且申请后不可变,我们假设输入字符串仅含非 ASCII 字符以确保缓冲区充足,返回时再截取有效部分(使用 subarray 引用,无需额外复制)。

完善

若编码时传入 GBK 不支持的字符,按上述逻辑将转换为 0 字符,因为 table 空缺位置默认为 0。而 0 本身属于 GBK 编码的一部分,故该实现尚不完善。

因此,可将 table 填充为其他特定值,查表时若出现该值,即可作为异常处理依据。

此外,根据百科资料,微软基于 GBK 实现的 Code page 936 额外包含一个 0x80 字码,对应字符为欧元符号 €。

经测试,即使非 Windows 系统的浏览器也支持该字符:

const gbkBuf = new Uint8Array([0x80])
new TextDecoder('gbk').decode(gbkBuf)   // "€"

演示:https://jsbin.com/vuxawul/edit?html,output

最终实现:https://github.com/EtherDream/str2gbk

采用此方案,仅需几十行代码和几百字节体积即可实现 GBK 编码,且性能表现优异。

以上就是JS字符串转GBK编码超精简实现详解的详细内容,更多关于JS字符串转GBK编码的资料请关注本站其它相关文章!

作者最新文章