1.正則表達式
UTF-16編碼方式源於UCS-2(Universal Character Set coded in 2 octets、2-byte Universal Character Set)。而UCS-2,是早期遺留下來的歷史產物。算法
UCS-2將字符編號直接映射爲字符編碼(CEF,而非CES,詳見前文中對現代字符編碼模型的解釋),亦即字符編號就是字符編碼,中間沒有通過特別的編碼算法轉換。所以,從現代字符編碼模型的角度來看的話,此時並無將編號字符集CCS與字符編碼方式CEF做嚴格區分,既能夠將UCS-2看做是編號字符集CCS中的字符編號,也能夠看做是字符編碼方式CEF中的字符編碼。編程
後來,隨着Unicode聯盟與ISO/IEC就建立全球統一的單一通用字符集進行合做,Unicode字符集與UCS字符集逐漸相互融合,二者最終基本保持了一致(詳見前文《刨根究底字符編碼之八——Unicode編碼方案概述》中的介紹)。編輯器
(笨笨阿林原創文章,轉載請註明出處)ide
2.編碼
這以後,Unicode逐漸佔據了主導地位,並引入了UTF-16編碼方式。爲何要引入UTF-16編碼方式呢?spa
前文已經介紹過了,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的增補字符,擴展勢在必行。code
3.
UCS於是又提出了UCS-4,即用四個字節共32位來表示一個字符(此時UCS-4一樣既可認爲是編號字符集CCS中的字符編號,也可認爲是字符編碼方式CEF中的字符編碼)。但碼元也所以從16位擴展到了32位。
而Unicode卻提出了不一樣的擴展方式——代理機制。具體而言,就是爲了能以一個統一的16位碼元同時編碼基本平面以及增補平面中的字符碼點編號,Unicode設計引入了UTF-16編碼方式,而且經過代理機制實現了擴展。
UTF-16編碼方式的引入,從現代字符編碼模型的角度來看的話,完全將編號字符集CCS與字符編碼方式CEF做了嚴格區分。也就是說,在UTF-16編碼方式中,編號字符集CCS中的字符編號與字符編碼方式CEF中的字符編碼再也不僅僅是簡單的直接映射關係。
具體來講,就是Unicode字符集基本平面BMP中的字符(大體至關於UCS字符集中的UCS-2字符,但必須除開U+D800~U+DFFF這一在Unicode字符集BMP中稱之爲代理碼點的部分),仍然是直接映射關係,亦即這部分字符的字符編號與字符編碼是等同的。
但Unicode字符集增補平面中的字符(大體至關於UCS字符集UCS-4字符中除開UCS-2字符的部分,由於廣義上的UCS-4字符實際上包含了UCS-2字符,固然狹義上的UCS-4字符不包括UCS-2字符),卻不是直接映射關係,而是必須經過代理機制這一編碼算法的轉換,亦即這部分字符的字符編號與字符編碼不是等同的。
所以,在Unicode引入了UTF-16編碼方式以後,站在現代字符編碼模型的角度上來看的話,再將UCS-2和UCS-4直接稱之爲字符編碼方式CEF已不是很合適,更多的應該是編號字符集CCS中的概念(固然,在瞭解其歷史緣由以後,將UCS-2和UCS-4同時理解爲編號字符集CCS和字符編碼方式CEF也何嘗不可);而若將UCS-2等同於UTF-16,將UCS-4等同於UTF-32(後文會有介紹),顯然也是不合適的。
(笨笨阿林原創文章,轉載請註明出處)
4.
UTF-16中的所謂代理機制,實際上就是用兩個對應於基本平面BMP代理區(Surrogate Zone)中的碼點編號的16位碼元來表示一個增補平面碼點,這兩個用來表示一個增補平面碼點的特殊16位碼元被稱之爲代理對(Surrogate Pair)(解釋詳見後文《UTF-16到底是如何編碼的——UTF-16的編碼算法詳解》)
UTF-16編碼方式及其代理機制是在Unicode 2.0中爲支持字符編號超過U+FFFF的增補字符而引入的,因而今後就由UCS-2的等寬(16位)碼元序列編碼方式(如前文所述,從現代字符編碼模型的角度來看的話,UCS-2更可能是的編號字符集CCS中的概念,但考慮到其歷史緣由,稱之爲字符編碼方式CEF亦何嘗不可,下同,再也不贅述),變成了UTF-16的變寬(16位或32位)碼元序列編碼方式。不過,碼元依然保持了16位不變。
5.
UCS-2所編碼的字符集中的U+D800~U+DFFF這部分代理碼點除外的話,UTF-16所編碼的字符集可當作是UCS-2所編碼的字符集的父集。
在沒有引入增補平面字符以前,UTF-16與UCS-2(U+D800~U+DFFF這部分代理碼點除外)的編碼徹底相同。但當引入增補平面字符後,UTF-16與UCS-2的編碼就不徹底相同了(事實上,因爲UCS-2只有兩個字節,根本沒法編碼增補平面字符)。
如今如有軟件聲稱本身支持UCS-2編碼,那至關因而在暗示其僅支持UCS字符集或Unicode字符集中的基本平面字符,而不能支持增補平面字符。
6.
因此說,UTF-16是變長編碼方式,每一個字符編碼爲2字節或4字節;而UCS-2是定長編碼方式,每一個字符編碼固定爲2字節。
另外,UTF-16中,大部分漢字採用兩個字節編碼,少許不經常使用漢字採用四個字節編碼。
Windows 2000及以後的版本是支持UTF-16的,以前的Windows NT/95/98/ME是隻支持UCS-2的。
(笨笨阿林原創文章,轉載請註明出處)
7.
做爲邏輯意義上的UTF-16編碼(碼元序列),因爲歷史的緣由,在映射爲物理意義上的字節序列時,分爲UTF-16BE(Big Endian)、UTF-16LE(Little Endian)兩種狀況。好比,「ABC」這三個字符的UTF-16編碼(碼元序列)爲:00 41 00 42 00 43;其對應的各類字節序列以下:
Windows平臺下的UTF-16編碼(即上述的FF FE 41 00 42 00 43 00) 默認爲帶有BOM的小端序(即Little Endian with BOM)。你能夠打開記事本,寫上ABC,保存時選擇Unicode(這裏的Unicode實際上指的是UTF-16 Little Endian with BOM,即帶BOM的UTF-16小端序CES編碼,詳見後文解釋)
而後保存,再用UltraEdit編輯器看看它的編碼結果:
Windows從NT時代開始就採用了UTF-16編碼方式,不少流行的編程平臺,例如.Net、Java、Qt還有Mac下的Cocoa等都是使用UTF-16做爲基礎的字符編碼。例如代碼中的字符串,在內存中相應的字節流就是UTF-16字節序列的。(注意,UTF-16編碼在Windows環境中被誤用爲「widechar」和「Unicode」的同義詞)
8.
UTF-16一方面使用變長碼元序列的編碼方式,相較於定長碼元序列的UTF-32算法更復雜(甚至比一樣是變長碼元序列的UTF-8也更爲複雜,由於引入了獨特的代理對這樣的代理機制);另外一方面仍然佔用過多字節,好比ASCII字符也一樣須要佔用兩個字節,相較於UTF-8更浪費空間和帶寬。
所以,UTF-16在Unicode字符集的三大編碼方式(UTF-8、UTF-16、UTF-32)中表現較爲糟糕。它的存在是歷史緣由形成的,引發了不少混亂。不過因爲其推出時間最先,已被應用於大量環境中,目前雖然不被推薦使用,但長期來看,做爲程序人員都不得不與之打交道。於是,對於其具體的編碼算法的瞭解是十分必要的,本系列文章的下一篇將詳細介紹其複雜的編碼算法(主要是代理編碼算法)。
(笨笨阿林原創文章,轉載請註明出處)
(未完待續)
【預告:本《刨根究底字符編碼》系列的下一篇將詳細介紹UTF-16複雜的編碼算法;而《刨根究底正則表達式》系列的下一篇將正式開始逐個正則表達式語法元素的詳解,敬請關注!】