如何將 ArrayBuffer 轉換為字串,或從 String 轉換

Renato Mangini

ArrayBuffers 可用來傳輸原始資料,且數個新的 API 皆仰賴這些 API,包括 WebSocketsWeb Intent 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) 和 WebWorkers。不過,由於憑證最近剛好進入 JavaScript 的環境,因此有時可能會遭到錯誤或濫用。

基本上,ArrayBuffer 只是透過特定遮罩觀看的位元組陣列。此遮罩 (ArrayBufferView 的執行個體) 會定義位元組對齊的方式,以符合內容的預期結構。舉例來說,如果您知道 ArrayBuffer 中的位元組代表 16 位元無正負號整數的陣列,只要將 ArrayBuffer 納入 Uint16Array 檢視畫面,即可使用括號語法操控其元素,就像 Uint16Array 是整數陣列一樣:

// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) {   // 258 === 0x0102
    console.log("ok");
}
bufView[0] = 255;    // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]

ArrayBuffer 的一個常見問題是如何將 String 轉換為 ArrayBuffer,反之亦然。由於 ArrayBuffer 實際上是位元組陣列,因此這項轉換需要雙方同意如何以位元組呈現字串中的字元。您可能之前曾看過這個「協議」,這是字串的字元編碼 (而一般「協議字詞」則是 Unicode UTF-16 和 iso8859-1)。因此,如果您和另一方已同意 UTF-16 編碼,轉換程式碼可能會像這樣:

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

請注意使用 Uint16Array。這是 ArrayBuffer 檢視畫面,會將 ArrayBuffers 的位元組對齊為 16 位元元素。它本身不會處理字元編碼本身,後者會由 String.fromCharCodestr.charCodeAt 以 Unicode 處理。

有一個常見的 StackOverflow 相關問題解答獲得熱票的問題,答案是一項重要的轉換解決方案:建立 FileReader 做為轉換器,並將含有字串的 Blob 提供給它。雖然這種方法有效,但可讀性不佳,而且我覺得速度很慢。由於人類史上曾發生許多無意識的疑慮,因此造成許多錯誤,因此我們要採取更具科學的方法。我已採用 jsperf 方法,結果確認我的假設。您可以在這裡查看示範

在 Chrome 20 版中,相較於使用 FileReader/Blob 方法,本文中的直接 ArrayBuffer 操作程式碼使用速度快了 27 倍。