JS 轉 gbk 編碼與 iconv-lite 探究

直接使用 api

const urlencode = require('urlencode');

const text = '你好';

urlencode.encode(text, 'gbk').toLocaleLowerCase();
// '%c4%e3%ba%c3'

urlencode.decode('%c4%e3%ba%c3', 'gbk')
// 你好

這個 urlencode 的庫是使用了 iconv-lite 的,其 encode 方法摘錄出來看是這樣的

var iconv = require('iconv-lite');
function encode(str, charset) {
  if (isUTF8(charset)) {
    return encodeURIComponent(str);
  }
    
  // 使用 iconv 轉爲 buffer
  var buf = iconv.encode(str, charset);
  var encodeStr = '';
  var ch = '';
  for (var i = 0; i < buf.length; i++) {
    // 從 buffer 裏按 16 進制拿出每個字節
    ch = buf[i].toString('16');
    if (ch.length === 1) {
      ch = '0' + ch;
    }
    // 拼接百分號
    encodeStr += '%' + ch;
  }
  encodeStr = encodeStr.toUpperCase();
  return encodeStr;
}

iconv-lite 可以把字符轉成 gbk 的 buffer ,但轉成 gbk 形式的 string 是沒有的。這是因爲,node 內部也不支持直接操作 GBK 字符串,瞧 Buffer.from(string[, encoding]) 第二個參數裏就無 gbk。

補充 gbk 編碼說明

'你'.charCodeAt(0) // 20320

let iconv = require('iconv-lite');

let buff = iconv.encode('你', 'GBK');

console.log(buff)
// Buffer(3) [196, 227]

爲什麼 Buffer 對象裏的十進制數字是 196 和 227?

參見 GBK 編碼大全,GBK 編碼中“你字”

C4 0 1 2 3 4 5 6 7 8 9 A B C D E F
A   摹 蘑 模 膜 磨 摩 魔 抹 末 莫 墨 默 沫 漠 寞
B 陌 謀 牟 某 拇 牡 畝 姆 母 墓 暮 幕 募 慕 木 目
C 睦 牧 穆 拿 哪 吶 鈉 那 娜 納 氖 乃 奶 耐 奈 南
D 男 難 囊 撓 腦 惱 鬧 淖 呢 餒 內 嫩 能 妮 霓 倪
E 泥 尼 擬 你 匿 膩 逆 溺 蔫 拈 年 碾 攆 捻 念 娘
F 釀 鳥 尿 捏 聶 孽 齧 鑷 鎳 涅 您 檸 獰 凝 寧

所以 “你” 這個字的高位是 0xC4 也就是十進制的 196,而低位是 0xE3 也就是十進制 227,“好” 這個字也同理。

補充說明:GBK 使用兩個字節進行表示,每個字節的第一位均爲1,這樣在高位字節和低位字節的值均大於127。

規定:但兩個大於127的字符連在一起時,就表示一個漢字,前面的一個字節(稱之爲高字節)從0xA1用到 0xF7,後面一個字節(低字節)從0xA1到0xFE,這樣可以組合出大約 7000 多個簡體漢字了。

在這些編碼裏,數學符號、羅馬希臘的字母、日文的假名們都被編進去了,連在 ASCII 裏本來就有的數字、標點、字母都統統重新編了兩個字節長的編碼,這就是常說的”全角”字符,而原來在 127 號以下的那些就叫”半角”字符了。

那 iconv-lite 是這麼做到轉 gbk 編碼的?

此庫內部建了一個從 utf-8 映射去 gbk 編碼的 map 表。

奧祕在於在 dbsc-data.js 裏有一段

'gbk': {
  type: '_dbcs',
  table: function() { return require('./tables/cp936.json').concat(require('./tables/gbk-added.json')) },
},

所以 gbk 的映射編碼來自於兩個 json 文件,cp936.json 與 gbk-added.json。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章