1)先啰嗦一下
UTF-16是一種編碼格式。啥是編碼格式?就是怎么存儲,也就是存儲的方式。 存儲啥?存二進制數字。為啥要存二進制數字? 因為Unicode字符集里面把二進制數字和字符一一對應了,存二進制數字就相當於存了二進制數字對應的字符了。為啥不直接存字符?因為計算機只能處理二進制數字。
UTF是 Unicode Translation Format的縮寫,即把Unicode轉做某種格式的意思
UTF-16跟UTF-8比較,好處在於大部分字符都以固定長度的字節 (2字節) 儲存,但UTF-16卻無法兼容ASCII編碼。
2)
utf-16編碼方式 ? 1.utf-16編碼方式源於ucs-2(universal character set coded in 2 octets、2-byteuniversal character set)。 而ucs-2,是早期遺留下來的歷史產物。 ucs-2將字符編號直接映射為字符編碼(cef,而非ces,詳見前文中對現代字符編碼模型的解釋),亦即字符編號就是字符編碼,中間沒有經過特別的編碼算法
Unicode字符集(CCS)到目前為止定義了包括1個基本平面BMP和16個增補平面SP在內的共17個平面。
每個平面的碼點數量為2^16=65536個,因此17個平面的碼點總數為共65536*17=1114112個。其中,基本平面碼點為65536個(碼點編號范圍為0x0000~0xFFFF),增補平面碼點為1114112-65536=65536*16=1048576個(碼點編號范圍為0x10000~0x10FFFF)。
很明顯,簡單地用一個16位碼元肯定無法表示所有17個平面的這么多碼點(因為2^16=65536,而碼點總數為65536*17=1114112)。而UCS-2,正是用兩個字節共16位來表示一個字符的。為支持字符編號超過U+FFFF的增補字符,擴展勢在必行。
UCS因而又提出了UCS-4,即用四個字節共32位來表示一個字符(此時UCS-4同樣既可認為是編號字符集CCS中的字符編號,也可認為是字符編碼方式CEF中的字符編碼)。但碼元也因此從16位擴展到了32位。
具體來說,就是Unicode字符集基本平面BMP中的字符(大致相當於UCS字符集中的UCS-2字符,但必須除開U+D800~U+DFFF這一在Unicode字符集BMP中稱之為代理碼點的部分),仍然是直接映射關系,亦即這部分字符的字符編號與字符編碼是等同的。
UTF-16中16的意思是16個bit的意思,也就是說是用16位來存儲,但是它比較奇葩,它使用 2 個或者 4 個字節來存儲
對於 Unicode 編號范圍在 0 ~ FFFF 之間的字符,UTF-16 使用兩個字節存儲,並且直接存儲 Unicode 編號,不用進行編碼轉換,這跟 UTF-32 非常類似。
對於 Unicode 編號范圍在 10000~10FFFF 之間的字符,UTF-16 使用四個字節存儲,具體來說就是:將字符編號的所有比特位分成兩部分,較高的一些比特位用一個值介於 D800~DBFF 之間的雙字節存儲,較低的一些比特位(剩下的比特位)用一個值介於 DC00~DFFF 之間的雙字節存儲。
位於 D800~0xDFFF 之間的 Unicode 編碼是特別為四字節的 UTF-16 編碼預留的,所以不應該在這個范圍內指定任何字符。如果你真的去查看 Unicode 字符集,會發現這個區間內確實沒有收錄任何字符。
UTF-16 要求在制定 Unicode 字符集時必須考慮到編碼問題,所以真正的 Unicode 字符集也不是隨意編排字符的
3)舉例UTF-16 兩個字節
用UTF-16表示"漢"
unicode是6C49(這是用十六進制表示,用十進制表示是27721,
UTF-16表示的話就是01101100 01001001(共16 bit,兩個字節).
4)大小端模式
文重點講解的是 UTF-16 編碼格式字節數組的轉化。UTF-16 顧名思義,就是用兩個字節表示一個字符。那么用兩個字節表示必然存在字節序的問題,即大端小端的問題。下面就來講講 UTF-16BE、UTF-16LE、UTF-16 三者之間的區別吧。
UTF-16BE,其后綴是 BE 即 big-endian,大端的意思。大端就是將高位的字節放在低地址表示。
UTF-16LE,其后綴是 LE 即 little-endian,小端的意思。小端就是將高位的字節放在高地址表示。
UTF-16,沒有指定后綴,即不知道其是大小端,所以其開始的兩個字節表示該字節數組是大端還是小端。即FE FF表示大端,FF FE表示小端。
采用UTF-16BE,UTF-16LE,一個字符編碼成兩個字節,采用UTF-16,一個字符編碼成4個字節,與UTF-16BE和UTF-16LE相比,在前邊加上了\uFEFF表示UTF-16BE,或加上\uFFEF表示UTF-16LE。
圖來自大小端模式百度百科
5)UTF-16 還有很多問題
- UTF-16 能表示的字符數有 6 萬多,看起來很多,但是實際上目前 Unicode 5.0 收錄的字符已經達到 99024 個字符,早已超過 UTF-16 的存儲范圍;這直接導致 UTF-16 地位頗為尷尬——如果誰還在想着只要使用 UTF-16 就可以高枕無憂的話,恐怕要失望了
- UTF-16 存在大小端字節序問題,這個問題在進行信息交換時特別突出——如果字節序未協商好,將導致亂碼;如果協商好,但是雙方一個采用大端一個采用小端,則必然有一方要進行大小端轉換,性能損失不可避免(大小端問題其實不像看起來那么簡單,有時會涉及硬件、操作系統、上層軟件多個層次,可能會進行多次轉換)
- 另外,容錯性低有時候也是一大問題——局部的字節錯誤,特別是丟失或增加可能導致所有后續字符全部錯亂,錯亂后要想恢復,可能很簡單,也可能會非常困難。(這一點在日常生活里大家感覺似乎無關緊要,但是在很多特殊環境下卻是巨大的缺陷)
目前支撐我們繼續使用 UTF-16 的理由主要是考慮到它是雙字節的,在計算字符串長度、執行索引操作時速度很快。當然這些優點 UTF-32 都具有,但很多人畢竟還是覺得 UTF-32 太占空間了。
這一點摘自https://www.cnblogs.com/fnlingnzb-learner/p/6163205.html
6)U+D800~U+DFFF
UTF-16還能表示一部分的UCS-4代碼點——U+10000~U+10FFFF。表示算法比較復雜,簡單說明如下:
- 從代碼點U中減去0x10000,得到U'。這樣U+10000~U+10FFFF就變成了 0x00000~0xFFFFF。
- 用20位二進制數表示U'。 U'=yyyyyyyyyyxxxxxxxxxx
- 將前10位和后10位用W1和W2表示,W1=110110yyyyyyyyyy,W2=110111xxxxxxxxxx,則 W1 = D800~DBFF,W2 = DC00~DFFF。
例如,U+12345表示為 D8 08 DF 45(UTF-16BE),或者08 D8 45 DF(UTF-16LE)。
但是由於這種算法的存在,造成UCS-2中的 U+D800~U+DFFF 變成了無定義的字符。