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

Renato Mangini

ArrayBuffer 用於傳輸原始資料,且有幾個新的 API 會依賴 ArrayBuffer,包括 WebSocketsWeb Intents 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 檢視畫面,可將 ArrayBuffer 的位元組對齊為 16 位元元素。它不會處理字元編碼本身,而是由 String.fromCharCodestr.charCodeAt 處理為 Unicode。

在 Stack Overflow 上,有一個熱門的相關問題,其中高票的解答提供了一個有點複雜的轉換解決方案:建立 FileReader 做為轉換器,並將包含字串的 Blob 餵入其中。雖然這個方法可行,但可讀性不佳,而且我懷疑它很慢。由於人類歷史上有許多錯誤都是源自於毫無根據的懷疑,因此我們在此採用更科學的方法。我使用 jsperf 測試這兩種方法,結果證實了我的懷疑,您可以查看這裡的示範

在 Chrome 20 中,使用本文的直接 ArrayBuffer 操控程式碼,速度比使用 FileReader/Blob 方法快上 27 倍。