JavaScript怎樣完成UTF-16編碼轉換為UTF-8編碼——utfx.js源碼剖析

概述

當你在前端須要經由過程二進制數據與服務端舉行通訊時,你可能會碰到二進制數據的編碼題目。大部份服務端的字符串編碼範例都為UTF-8,而JavaScript中字符串編碼範例是UTF-16,因而,你須要一個能夠將字符串在兩種編碼體式格局間舉行轉換的要領。

本文經由過程對utfx.js這個庫的代碼舉行剖析,帶人人深切相識UTF8和UTF16這兩種編碼體式格局在JavaScript中的轉換要領,同時加深對Unicode中UTF-8和UTF-16兩種編碼體式格局的詳細道理的明白。

本文的主要內容為:

  • utfx.js API簡樸引見
  • UTF-16編碼轉換為UTF-8編碼
  • UTF-8編碼字符串長度盤算
  • 試驗性功能:window.TextEncoder

假如有讀者不相識Unicode中UTF-8和UTF-16兩種編碼體式格局的詳細道理,能夠瀏覽我的前一篇博客——Unicode中UTF-8與UTF-16編碼詳解

假如有讀者想要相識該庫相干的轉換運用場景,能夠瀏覽我之前的博客WebSocket系列之JavaScript字符串怎樣與二進制數據間舉行相互轉換

utfx.js API簡介

在舉行詳細的代碼詳解之前,我們先來相識下我們須要引見的庫——utfx.js。我們只要相識了這個庫的運用要領,我們才能夠更好的明白源碼。

utfx.js代碼不多,一共只要八個API接口,分別為:

  • encodeUTF8:將UTF-8編碼的字符串code碼轉換為二進制bytes。
  • decodeUTF8:將UTF-8編碼的二進制bytes解碼城字符串code碼。
  • UTF16toUTF8:將UTF-16的字符轉換為UTF-8的code碼。
  • UTF8toUTF16:將UTF-8的code碼轉換為UTF-16的字符。
  • encodeUTF16toUTF8:將UTF-16編碼的字符轉換為UTF-8編碼的bytes。
  • decodeUTF8toUTF16:將UTF-8編碼的bytes轉換為UTF-16編碼的字符。
  • calculateCodePoint:盤算UTF-8編碼下的字符長度。
  • calculateUTF8:盤算須要用來存儲UTF-8編碼code碼的bytes的長度。
  • calculateUTF16asUTF8:盤算UTF-16編碼的字符在轉換成UTF-8后須要的存儲長度。

下面,我們將遴選幾個具有代表性的API,針對其完成的詳細代碼來舉行剖析,協助人人疾速明白這兩種編碼體式格局。

UTF-16編碼轉換為UTF-8編碼

下面讓我們來看下怎樣將UTF-16編碼的數據轉換為UTF-8編碼的數據。

當我們須要把UTF-16的數據轉換為UTF-8編碼的數據時,最好的要領肯定是將UTF-16編碼的數據轉換為通用的Unicode碼,在舉行UTF-8編碼。我們經由過程UTF16toUTF8和encodeUTF8要領的代碼來舉行詳細剖析。

UTF16toUTF8

這個函數名看上去是直接將UTF-16編碼的bytes數據轉換為UTF-8編碼的的Bytes數據。實際上是,將UTF-16編碼的bytes數據轉換為Unicode對應的二進制數據

/**
 * UTF16數據轉換到Unicode數據
 * @param src 數據源,範例為Function,挪用一次返回1 Byte數據,假如抵達字符串末端則返回null
 * @param dst 處置懲罰函數,範例為Function,獲得的Bytes作為參數傳遞給dst函數
 */
utfx.UTF16toUTF8 = function (src, dst) {
    var c1, c2 = null;
    while (true) {
        // 抵達末端挪用src函數獲得null後會進入此分支邏輯
        if ((c1 = c2 !== null ? c2 : src()) === null)
            break;
        
        //Unicode範例劃定,U+D800~U+DFFF的值不對應任何字符,即特地用來推斷是不是為高位代辦
        if (c1 >= 0xD800 && c1 <= 0xDFFF) {
            if ((c2 = src()) !== null) {
                // 假如Unicode碼局限凌駕U+FFFF則會進入此分支邏輯(兩段:第一段大於U+D800,第二段大於U+DC00)
                if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
                    // 第一步:用c1復原高10位;第二步:用c2復原低十位;第三步:加上減去的0x10000
                    dst((c1 - 0xD800) * 0x400 + c2 - 0xDC00 + 0x10000);
                    c2 = null; continue;
                }
            }
        }
        dst(c1);
    }
    if (c2 !== null) dst(c2);
};

依據代碼和上面的解釋,人人應當就可以看懂對應代碼,因而在此不做過量贅述。我們接着看將Unicode碼轉換為UTF-8編碼的要領。

encodeUTF8

該要領是將Unicode碼舉行UTF-8編碼轉換,從而獲得UTF-8編碼的Bytes數據。

/**
 * Unicode數據轉換為UTF-8數據
 * @param src 數據源,範例為Function,挪用一次返回1 Byte數據,假如抵達字符串末端則返回null
 * @param dst 處置懲罰函數,範例為Function,獲得的Bytes作為參數傳遞給dst函數
 */
utfx.encodeUTF8 = function (src, dst) {
    var cp = null;
    if (typeof src === 'number')
        cp = src,
            src = function () {return null;};
    while (cp !== null || (cp = src()) !== null) {
        if (cp < 0x80)
        // 1 byte存儲狀況
            dst(cp & 0x7F);
        else if (cp < 0x800)
        // 2 byte存儲狀況
            dst(((cp >> 6) & 0x1F) | 0xC0),
            dst((cp & 0x3F) | 0x80);
        else if (cp < 0x10000)
        // 3 byte存儲狀況
            dst(((cp >> 12) & 0x0F) | 0xE0),
            dst(((cp >> 6) & 0x3F) | 0x80),
            dst((cp & 0x3F) | 0x80);
        else
        // 4 byte存儲狀況
            dst(((cp >> 18) & 0x07) | 0xF0),
            dst(((cp >> 12) & 0x3F) | 0x80),
            dst(((cp >> 6) & 0x3F) | 0x80),
            dst((cp & 0x3F) | 0x80);
        cp = null;
    }
};

上面的代碼與UTF-8編碼範例中的體式格局基礎一致,假如沒有明白相干範例,能夠先瀏覽本文概述中提到的前一篇博客。

編碼字符串長度盤算

當我們給出一串Unicode碼時,我們須要曉得請求多大的ArrayBuffer來舉行轉換后的數據存儲。恰好,這個庫還供應了依據Unicode碼的長度或許UTF-16編碼花樣的數據來盤算UTF-8數據的存儲長度。

下面我們來引見calculateUTF8calculateUTF16asUTF8這兩個要領。

calculateUTF8

該要領是經由過程Unicode碼來盤算轉換為UTF-8編碼后所佔存儲長度。

/**
 * 依據Unicode編碼來盤算轉換成UTF-8編碼后須要的存儲長度
 * @param src 數據源,範例為Function,挪用一次返回1 Byte數據,假如抵達字符串末端則返回null
 */
utfx.calculateUTF8 = function (src) {
    var cp, l = 0;
    while ((cp = src()) !== null)
        // 佔1 Byte的局限是0~0x7F;佔2 Byte的局限是0x80~0x7FF;佔三個字節的局限是0x800~0xFFFF;佔4個字節的局限為:0x10000~0x10FFFF
        l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;
    return l;
};

依據上面的的代碼和UTF-8的編碼範例,我們就可以夠很輕易明白這類寬度盤算的要領。

calculateUTF16asUTF8

該要領是經由過程UTF16的數據來盤算轉換為Unicode碼和轉換為UTF-8編碼后所佔存儲長度。

/**
 * 依據UTF-16編碼的Bytes來盤算轉換為Unicode的長度和轉換成UTF-8編碼后須要的存儲長度
 * @param src 數據源,範例為Function,挪用一次返回1 Byte數據,假如抵達字符串末端則返回null
 */
utfx.calculateUTF16asUTF8 = function (src) {
    var n = 0, l = 0;
    utfx.UTF16toUTF8(src, function (cp) {
        ++n; l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;
    });
    return [n, l];
};

該要領經由過程之前引見的將UTF-16編碼轉換為Unicode碼的要領獲取到Unicode數據,再舉行盤算,返回了Unicode碼的長度和UTF-8編碼后長度。

window.TextEncoder與Window.TextDecoder

這是兩個處在試驗性的新組織函數,經由過程建立編碼器(TextEncode對象)和解碼器(TextDecode對象)來完成JavaScript中string範例與UTF-8編碼數據中的相互轉換。

組織要領將會返回一個UTF-8編碼的,運用要領以下:

let encoder = new TextEncoder();
let decoder = new TextDecoder();

let unit8Array = encoder.encode('a'); // 返回一個Unit8Array範例——[97]
let str = decoder.decode(arr); // 返回一個值為'a'的字符串

現在,這項新技術的的兼容性依然存在很大題目,只要Chrome 38、Firefox 19以及Opera 25以上才支撐,其他主流的瀏覽器如IE和Safari都還沒有任何支撐,因而在臨盆環節中須要鄭重運用。

總結

本文對完成了Unicode中UTF-8和UTF-16這兩種編碼體式格局的庫——utfx.js舉行了部份代碼剖析。經由過程看到詳細的代碼完成,置信人人應當能夠更加好的明白這兩種編碼體式格局的詳細範例,以及對應的運用體式格局和場景。

    原文作者:hjava
    原文地址: https://segmentfault.com/a/1190000014398200
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞