天天看點

前端中JS的字元編碼及常用操作字元API

作者:第1024次Debug

在 JavaScript 中,Unicode 編碼方式用于表示字元,包括中文字元。Unicode 是一個标準的字元編碼系統,為世界上幾乎所有的字元都定義了唯一的編号,以便在計算機中進行交換和處理。本文将着重讨論 JavaScript 中的字元編碼。關于其前端資料流的一些編碼工作可以移步這裡: JS處理檔案資料的API

前端中JS的字元編碼及常用操作字元API

字元集簡介(Unicode)

Unicode 是一個字元編碼标準,定義了世界上幾乎所有字元的唯一編号,使得在計算機中交換和處理文本變得更加統一和可靠。Unicode 字元集包含了數十萬個字元,覆寫了幾乎所有的書寫系統、符号、标點符号、表情符号等。以下是 Unicode 字元集的詳細内容:

  • 基本多文種平面(Basic Multilingual Plane,BMP): 這是Unicode字元集中最常用和最廣泛使用的部分,包含了U+0000至U+FFFF的字元範圍,共計65536個字元。其中包括了:
  • 基本拉丁字母(Basic Latin):包含英文字母、數字、标點符号等,U+0000至U+007F。
  • 拉丁字母補充-1(Latin-1 Supplement):包含一些歐洲語言特有的字元,U+0080至U+00FF。
  • 拉丁字母擴充-A、B、C、D(Latin Extended-A、B、C、D):包含更多歐洲語言的字元,U+0100至U+024F。
  • 基本希臘字母(Greek and Coptic):包含希臘字母,U+0370至U+03FF。
  • 基本西裡爾字母(Cyrillic):包含西裡爾字母,U+0400至U+04FF。
  • 基本漢字(CJK Unified Ideographs):包含最常用的漢字,U+4E00至U+9FFF。
  • 以及其他語言的字元,如希伯來文、阿拉伯文、泰文、韓文、日文假名等。
  • 輔助平面(Supplementary Planes): 除了基本多文種平面之外,Unicode還定義了16個輔助平面,分别從U+010000至U+10FFFF,每個輔助平面包含65536個字元,共計1,114,112個字元。輔助平面中包含了更多不常用的字元,特殊符号、曆史上的書寫系統、emoji表情符号等。
  • 保留區域(Private Use Area): Unicode還保留了一部分區域,供個人或組織自行定義字元。這些字元不在Unicode官方标準中定義,隻在特定的上下文中有意義。
  • 非字元代碼點(Noncharacter Code Points): Unicode還定義了一些非字元代碼點,這些代碼點沒有對應的字元表示,用于特殊目的,如表示無效的編碼或保留未來使用。

注: JavaScript 中的使用的 Unicode 編碼方式主要有兩種:UTF-16 和 UTF-8。

UTF-16

UTF-16(16-bit Unicode Transformation Format)是JavaScript中使用的主要字元編碼方式。在UTF-16中,每個字元由一個或兩個16位的代碼單元(code unit)表示。對于 ASCII 字元和大部分常用字元,UTF-16使用一個16位的代碼單元表示;對于一些較少使用的字元(如一些特殊的中文字元),UTF-16使用兩個16位的代碼單元表示,合稱為“代理對”(surrogate pair)。UTF-16 代理對的範圍為:從 U+D800 到 U+DFFF。注意,這個範圍是不可用的(reserved),并且不能用于表示 Unicode 字元,是以UTF-16的可用編碼範圍是從 U+0000到 U+D7FF 和從 U+E000 到 U+10FFFF。這樣留出了一個區間用于表示代理對。

注意:無法列印顯示不代表不表示字元,如 U+E000 - U+FFFF 在Unicode編碼中屬于字形圖形(Private Use Area)範圍,有一些字元可以在浏覽器中列印出來,但需要注意的是這些字元的顯示效果可能會因浏覽器、作業系統和字型的差異而有所不同。這些字元沒有統一的含義,通常用于特定的定制和私有應用場景

UTF-16代理對編碼規則如下:

  • 高位代理(High Surrogate):U+D800到U+DBFF,共1024個代碼點。 高位代理的二進制範圍為:1101 10xx xxxx xxxx
  • 低位代理(Low Surrogate):U+DC00到U+DFFF,共1024個代碼點。 低位代理的二進制範圍為:1101 11xx xxxx xxxx

在JavaScript中,可以使用 \u 轉義序列來表示UTF-16編碼的字元,後跟四個十六進制數字。例如,中文字元 “你” 對應的UTF-16編碼是U+4F60,可以用 \u4F60 來表示。

js// 單個16位代碼單元表示的字元
const chineseCharacter = '\u4F60'; // 表示中文字元"你"
console.log(chineseCharacter); // 輸出:你

// 雙個16位代碼單元表示的字元(使用高低代理對表示)
const chineseCharacter = '\ud841\udf0e''; // 表示中文字元""
console.log(chineseCharacter); // 輸出:           

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一種變長的編碼方式,它可以用一個或多個8位位元組(byte)來表示一個字元。UTF-8編碼的主要特點是使用1到4個位元組來表示不同範圍的Unicode字元,使得它能夠适應不同類型的文本,并且相容ASCII字元。對于一般的中文字元,多數是使用3個位元組來表示。

UTF-8編碼規則如下:

單位元組編碼:

  • 對于ASCII字元(U+0000至U+007F),UTF-8使用單個位元組來表示,位元組的最高位為0,其餘7位表示ASCII字元的編碼。

多位元組編碼: 對于非ASCII字元(U+0080及以上的字元),UTF-8使用多個位元組表示。多位元組的編碼規則如下:

  • 2位元組編碼:第一個位元組的前兩位為“110”,第二個位元組的前兩位為“10”,剩下的5+6位用來存儲Unicode字元的編碼。
  • 3位元組編碼:第一個位元組的前三位為“1110”,第二、第三個位元組的前兩位為“10”,剩下的4+6+6位用來存儲Unicode字元的編碼。
  • 4位元組編碼:第一個位元組的前四位為“11110”,第二、第三、第四個位元組的前兩位為“10”,剩下的3+6+6+6位用來存儲Unicode字元的編碼。

字元編碼常用的API

1. String.fromCharCode

根據給定碼點,查找相對應的字元,注意:該Api隻支援 16 位 Unicode 碼點,也即是到0 ~ 65535 範圍的字元,可能無法正确處理某些特殊字元。

jsString.fromCharCode('65532')   //  ''
String.fromCharCode('2312')   // 'ई'

String.fromCharCode(21029234827265) // '态'   超出範圍時 會保留後兩個位元組的資料
// 21029234827265 二進制表示為  100110010000001000000000100100110000000000001
// 後兩個位元組 為 '0110000000000001' 轉成10進制為 24577
// 是以 String.fromCharCode(21029234827265) === String.fromCharCode(24577)  // '态' 
           

2. String.fromCodePoint

根據給定碼點,查找相對應的字元,注意:該Api支援所有Unicode字元的範圍。

jsString.fromCodePoint('132878') //    超過了 0xffff範圍
String.fromCodePoint('24577')  // 态    沒超過了 0xffff範圍
           

下面三個 API 都是根據字元查找相關的碼點位置,有所差別

3. String.prototype.charCodeAt

取指定索引處字元的 Unicode 碼點值。 注意:該Api隻支援 16 位 Unicode 碼點,也即是到0 ~ 65535 範圍的字元,并且取得字元位置的編碼方式是以UTF-16格式擷取,如下。

js // 沒超過了 0xffff範圍
String.prototype.charCodeAt.call('态',0)   // 24577  0x6001

// 超過了 0xffff範圍
String.prototype.charCodeAt.call('',0)   // 55361  0xd841
String.prototype.charCodeAt.call('',1)   // 57102  0xdf0e
           

4. String.prototype.charAt

取指定索引處字元的 Unicode 碼點值。 注意:該Api隻能按照 16 位 去取值,隻支援 16 位 Unicode 碼點的一次性取值。

js// 超過了 0xffff範圍
String.prototype.charAt.call('$%^&*',0)   // '\uD841'  與 String.prototype.charCodeAt.call('',0) 位置相同
String.prototype.charAt.call('$%^&*',1)   // ''\uDF0E'  與 String.prototype.charCodeAt.call('',1) 位置相同
// 沒超過了 0xffff範圍
String.prototype.charAt.call('$%^&*',2)   // '#39;
           

5. String.prototype.codePointAt

擷取指定索引處的字元。 來處理更大範圍的 Unicode 碼點值,可以包含整個unicode編碼的字元, 注意:該Api支援所有 Unicode 字元的範圍。

jsString.prototype.codePointAt.call('') // 132878   超過了 0xffff範圍
String.prototype.codePointAt.call('态')  // 24577    沒超過了 0xffff範圍
           

UniCode字元到UTF-16轉換

由于需要在浏覽器顯示相關的Unicode字元,使用類似 '\u6001' 這種形式,我們需要對Unicode上的所有碼點進行處理,方式有很多,這裡使用較為簡單的一種方式實作,如下:

jsfunction getCodePointByCharsToChar(chars){
    const res = [];
    for(let i =0; i<chars.length;i++ ){
        const high  =chars[i].charCodeAt(0).toString(16);
        if(chars[i]){
            if(chars[i].codePointAt()>0xffff){
                const low  =chars[i].charCodeAt(1).toString(16);
                res.push(
                    `\u${'0'.repeat(4-high.length)}${high}\u${'0'.repeat(4-low.length)}${low}`
                );
            }else{
                res.push(
                    `\u${'0'.repeat(4-high.length)}${high}`
                );
            }
        }
        
    }
    return res;
}
// 雙位元組字元表示
getCodePointByCharsToChar('今天是個好天氣')  // ['\u4eca', '\u5929', '\u662f', '\u4e2a', '\u597d', '\u5929', '\u6c14']


// 多位元組字元表示
getCodePointByCharsToChar('')  // ['\ud841', '\udf0e', '\ud841', '\udf0e', '\ud841', '\udf0e', '\ud841', '\udf0e', '\ud841', '\udf0e', '\ud841', '\udf0e']
           

總結: 關于前端中常常使用的字元集相關的API介紹已經結束,關于Javascript字元集的一些想法和問題,可以評論區留言。

前端中JS的字元編碼及常用操作字元API

繼續閱讀