Unicode 字元轉換為 UTF16
剛好最近都在看編碼的東西,順便整理一下
Unicode
Unicode 又稱萬國碼,是一種電腦編碼規則,目的是為了解決不同語言之間的文字互通,目前,幾乎所有電腦系統都支援基本拉丁字母,並各自支援不同的其他編碼方式。Unicode 為了和它們相互相容,其首 256 個字元保留給 ISO 8859-1(也就是現在大家所熟知的,以 ASCII 為基礎,再添加附加符號、拉丁字母)所定義的字元
Unicode 字元的平面對應
目前 Unicode 分為 17 個平面,每個平面包含 65536 個字元,每個平面的字元都有一個獨一無二的點位,這個點位就是該字元的 Unicode 點位,這個點位是一個十六進位的數字,可以用來表示該字元,目前只用了少數平面,主要的就是前四個平面,其他的平面則是用來存放一些不常用的字元,或是一些特殊的字元
平面 | 範圍 | 中文名 | 英文名 | 簡稱 |
---|---|---|---|---|
0 | U+0000 至 U+FFFF | 基本多文種平面 | Basic Multilingual Plane | BMP |
1 | U+10000 至 U+1FFFF | 多文種補充平面 | Supplementary Multilingual Plane | SMP |
2 | U+20000 至 U+2FFFF | 表意文字補充平面 | Supplementary Ideographic Plane | SIP |
3 | U+30000 至 U+3FFFF | 表意文字第三平面 | Tertiary Ideographic Plane | TIP |
ref:Unicode 字元平面對映
Unicode 的編碼
在表示一個 Unicode 字元的時候,會使用 U+4AE0 這樣的格式來表示,其中 U+ 是固定的,後面的數字則是該字元的 Unicode 點位。
UTF8
UTF8 的英文是 8-bit Unicode Transformation Format,是一種可以變動長度的字元表示方式
- 128 個 ASCII 字元,只需一個位元組編碼(Unicode 範圍由 U+0000 至 U+007F)
- 其他西歐國家的會用到的一些字元,少部分則需要用到兩個位元組(Unicode 範圍由 U+0080 至 U+07FF)
- 其他大部分的常用字元,包含中文、日文、韓文等,則需要用到三個位元組(Unicode 範圍由 U+0800 至 U+FFFF)
- 其他極少使用的 Unicode 輔助平面的字元(也就是 BMP 之外的輔助平面)使用四至六位元組編碼(Unicode 範圍由 U+10000 至 U+1FFFFF 使用四位元組,Unicode 範圍由 U+200000 至 U+3FFFFFF 使用五位元組,Unicode 範圍由 U+4000000 至 U+7FFFFFFF 使用六位元組)。
ref: 維基百科 UTF-8
UTF8 與 UTF16 的比較
在輔助平面字元的處理,兩者都需要使用 4 bytes 表示一個字元;但在 BMP 平面的字元處理上,UTF-8 使用 1-3 bytes 表示一個字元,而 UTF-16 則是固定使用 2 bytes 表示一個字元,這也是為什麼 UTF16 在處理 BMP 平面的字元時,會比 UTF8 來的節省空間
Unicode 點位轉換的計算過程 (輔助平面的字元)
𠎀
這個字的 Unicode 點位: 0x20380
首先需要將其扣除 0x10000,因為該點位超過了 0xFFFF,並不屬於 BMP 範圍,所以需要使用 4 個 byte 來表示,扣除掉 0x10000 後,剩下的點位就是該字的編碼
該字的 Unicode 點位為: 0x20380 - 0x10000 = 0x10380,也就是二進位 0001000000 1110000000
將其切割為兩部分,先拿低點位的 10 碼,剩下高點位不足的部分補 0,如此一來就會得到兩個 10 位元組的碼位,
- 高點位:
0001000000
- 低點位:
1110000000
輔助平面的高低點位初始值為 0xD800
與 0xDC00
,所以將剛剛的計算出來的點位加上這兩個值就是該字的編碼
這個初始值,轉為二進位後有 6 碼,再加上切割出來的 10 碼,剛好就是一個 16 位元組的字元
高點位的計算為 0xD800,也就是 110110 0000000000
,加上剛剛切割出來的高位十碼 = 110110 0001000000
= 0xD840
低點位的計算為 0xDC00,也就是 110111 0000000000
,加上剛剛切割出來的低位十碼 = 110111 1110000000
= 0xDF80
為了要表示這一個字將其轉為二進位後,總共使用了 4 個 byte,也就是 16 個位元組 11011000 01000000 11011111 10000000
所以 𠎀
的編碼為 0xD840 0xDF80
一般來說瞭解這種東西也沒有甚麼用,但如果有機會接觸到編碼問題,多知道一些細節對於偵錯會有幫助
ReCap
- Unicode 是把所有的字元都統一編碼,目的是為了解決不同語言之間的文字互通
- 在電腦上要處理文字,通常都採用 UTF8,因為更省空間
- UTF16 並沒有比 UTF8 更好,兩者差異只在於如何編碼 BMP 平面的字元,對輔助平面的字元,兩者都需要使用 4 bytes 來表示
- 中文難字、EMOJI 等字元,通常都是在輔助平面,所以需要使用 4 bytes 來表示,計算過程可參考上述的範例
延伸:字元計算
有的時候會需要限制使用者的輸入字元數,可能是因為 SMS 的字元限制,也可能是因為要開資料庫欄位長度的限制,都會需要計算字元數、或者是 byte 數,這時候就可以使用 JavaScript 的 TextEncoder 來計算字元所佔用的 byte 數
下列範例,取自IT Tools
1 | <template> |
當中的 Character count 就是使用者輸入的字元長度,直接使用 text.length 即可;Byte size 經由TextEncoder計算,就算使用者輸入了 emoji 也可以正確的計算使用的 byte 數
1 | export function getStringSizeInBytes(text: string) { |