WebSocket系列之JavaScript字串如何與二進位制資料間進行互相轉換

黃Java發表於2019-03-04

概述

上一篇部落格我們說到了如何進行數字型別(如Short、Int、Long型別)如何在JavaScript中進行二進位制轉換,如果感興趣的可以可以閱讀本系列第二篇部落格——WebSocket系列之JavaScript中數字資料如何轉換為二進位制資料。這次,我們來說下string型別的資料如何進行處理。
本文是WebSocket系列的第三篇,主要介紹string資料與二進位制資料之間的轉換方法,具體的內容如下:

  • JavaScript中string型別基礎知識
  • JavaScript如何將string型別轉換為二進位制資料
  • JavaScript如何將二進位制資料轉換為string型別
    本文與WebSocket並無太強關聯,不過作為在WebSocket中傳遞二進位制資料的基礎知識儲備,因此放入了此係列當中。
    如果讀者對WebSocket並不瞭解,或者說不明白它的使用場景和細節,可以閱讀我的本系列的第一篇部落格——WebSocket系列之基礎知識入門篇

string型別基礎知識

string這個型別,對於熟悉JavaScript的同學應該都不陌生,它是屬於JavaScript中基礎資料型別的一種。不過,我們今天要先介紹下DOMString

DOMString 是一個UTF-16字串。由於JavaScript已經使用了這樣的字串,所以DOMString 直接對映到 一個String。將null傳遞給接受DOMString的方法或引數時通常會把其轉換成為“null”。

在WebSocket中進行string型別資料傳輸時,使用的其實也是DOMString。不過,根據MDN中對DOMString的介紹我們可以瞭解到,大部分日常使用場景中,我們可以認為DOMString就是一個string型別。所以,我們只需要瞭解此型別,使用時仍然當成string型別處理即可。

編碼

既然上面提到了UTF-16,那麼我們就來簡單介紹下UTF-16,以及後端常用的UTF-8這兩種編碼方式。
為什麼需要介紹編碼型別呢?因為我們在與後端進行字串資料傳遞時,可能使用的編碼方式不同,這樣就會導致雙方得到不同的資料。因此,我們在進行string的二進位制資料通訊時,不僅僅需要將字串轉換成二進位制,還需要協商一致的string編碼。

UTF-16

UTF-16 (16-bit Unicode Transformation Format) 是Unicode字元編碼五層次模型的第三層:字元編碼表(Character Encoding Form,也稱為”storage format”)的一種實現方式。即把Unicode字符集的抽象碼位對映為16位長的整數(即碼元)的序列,用於資料儲存或傳遞。
Unicode字元的碼位,需要1個或者2個16位長的碼元來表示,因此這是一個變長表示。

UTF-16是JavaScript中的string編碼型別。

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼,也是一種字首碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII相容,這使得原來處理ASCII字元的軟體無須或只須做少部分修改,即可繼續使用。UTF-8使用一至四個位元組為每個字元編碼(2003年11月重新規範)。

UTF-8是很多語言使用的通用編碼型別,在後端應用中非常常見。

JavaScript中UTF16和UTF-8如何進行編碼轉換

在Github上有轉換庫GitHub – dcodeIO/utfx: A compact library to encode, decode and convert UTF8 / UTF16 in JavaScript.,通過這個庫,可以將字串在UTF-8編碼和UTF-16編碼中進行轉換。該庫的具體原理和內容以及兩種編碼方式的詳細內容說明將會在之後的部落格中進行講解。我們下面簡單的介紹下它是使用方式:

import utfx from `./util/utfx`;

let str = `abcdefg`;
let result = [];

function stringSource(s) {
    let i = 0;
    return function () {
        return i < s.length ? s.charCodeAt(i++) : null;
    };
}

utfx.encodeUTF16toUTF8(stringSource(str), function (b) {
    result.push(b);
}.bind(this));
複製程式碼

同理,該類庫提供了其他的方法:

  • decodeUTF8toUTF16,將UTF-8的string資料轉換為UTF-16的string資料。
  • calculateUTF16asUTF8,計算UTF-16編碼的string型別型別轉換為UTF-8後所佔Byte長度。
    這兩個方法我們在之後的章節中也會用到。

JavaScript如何將string型別轉換為二進位制資料

瞭解了JavaScript中string型別的編碼和在UTF-8和UTF-16之間轉換編碼的方式,下面我們來看下如何將string型別轉換為二進位制資料。
首先,我們假定與後端互動時使用的編碼方式為UTF-8,這樣能夠滿足更多的使用場景。如果仍然使用UTF-16的話,則直接忽略轉換編碼的邏輯即可。
簡單介紹下實現思路:我們得到一個需要轉換的字串後,先知道其長度後,初始化ArrayBuffer中相關引數,將資料放入ArrayBuffer中即可。我們將上面的示例稍作改動:

import utfx from `./util/utfx`;

function stringSource(s) {
    var i = 0;
    return function () {
        return i < s.length ? s.charCodeAt(i++) : null;
    };
}

let str = `abcdefg`;

let strCodes = stringSource(str);
let length = utfx.calculateUTF16asUTF8(strCodes)[1];
let buffer = new ArrayBuffer(length + 4); // 初始化長度為UTF8編碼後字串長度+4個Byte的二進位制緩衝區
let view = new DataView(buffer);
let offset = 4;

view.setUint32(0, length); // 將長度放置在字串的頭部

utfx.encodeUTF16toUTF8(stringSource(str), function (b) {
    view.setUint8(offset++, b);
}.bind(this));
複製程式碼

通過上面的示例,我們就已經將一個二進位制資料根據UTF-8編碼後放入了ArrayBuffer中,同時,將其長度作為一個Unsigned Int型別儲存在了二進位制頭部4個Byte的位置。

JavaScript如何將二進位制資料轉換為string型別

知道了如何將string型別轉換為二進位制資料,下面我們看下如何將整個資料從二進位制中讀取,轉換回string型別。
根據上面轉換為二進位制的過程,我們不難想到相關的二進位制轉string型別方法。具體示例如下:

import utfx from `./util/utfx`;
let str = `abcdefg`;

function stringSource(s) {
    var i = 0;
    return function () {
        return i < s.length ? s.charCodeAt(i++) : null;
    };
}

let strCodes = stringSource(str);
let length = utfx.calculateUTF16asUTF8(strCodes)[1];
let buffer = new ArrayBuffer(length + 4); // 初始化長度為UTF8編碼後字串長度+4個Byte的二進位制緩衝區
let view = new DataView(buffer);
let offset = 4;

// 字串轉換二進位制過程
view.setUint32(0, length); // 將長度放置在字串的

utfx.encodeUTF16toUTF8(stringSource(str), function (b) {
    view.setUint8(offset++, b);
}.bind(this));

// 二進位制轉換字串過程
let Strlength = view.getUint32(0);
offset = 4;
let result = []; // Unicode編碼字元
let end = offset + Strlength;


utfx.decodeUTF8toUTF16(function () {
    return offset < end ? view.getUint8(offset++) : null; // 返回null時會退出此轉換函式
}.bind(this), (char) => {
    console.log(char)
    result.push(char);
});

let strResult = result.reduce((prev, next)=>{
    return prev + String.fromCharCode(next);
}, ``);
複製程式碼

通過上面的示例我們可以知道,我們只需要在前面4個Byte中將字串長度讀取出來,然後再從第4個Byte(從0開始算)的位置開始讀取指定長度的字串字元編碼即可。最後,我們得到了一個Unicode碼陣列,只需要fromCharCode方法即可將其轉換為字串。

總結

通過使用ArrayBuffer和DataView,我們能夠在string資料和二進位制資料中進行互相轉換。
有了string型別轉換的相關基礎,讀者就能夠在之後的WebSocket進行二進位制資料傳遞時理解相關的內容和處理邏輯。
下一篇WebSocket系列相關的部落格,將會介紹如何通過WebSocket來向後端傳遞二進位制資料,以及如何處理通過WebSocket收到的二進位制資料。有興趣的同學可以繼續關注。

相關文章