Unicode 編碼

今天妒蔚,在學(xué)習(xí) Node.js 中的 Buffer 對象時,注意到它的 alloc 和 from 方法會默認(rèn)用 UTF-8 編碼屑埋,在數(shù)組中每位對應(yīng) 1 字節(jié)的十六進(jìn)制數(shù)肆汹。想到了之間學(xué)習(xí) ES6 時關(guān)于字符串的 Unicode 表示法,突然就很想知道 UTF-16 是如何進(jìn)行編碼的输虱,我嘗試將一些漢字轉(zhuǎn)換成二進(jìn)制數(shù)些楣,然后簡單的按 2 個字節(jié)一組轉(zhuǎn)換成十六進(jìn)制,發(fā)現(xiàn)對于那些碼點較大的漢字宪睹,結(jié)果并不僅僅是簡單的二進(jìn)制轉(zhuǎn)十六進(jìn)制愁茁。于是,我開始在網(wǎng)上找資料亭病,決心徹底弄明白 Unicode 編碼鹅很。

Unicode編碼則是采用雙字節(jié)16位來進(jìn)行編號,可編65536字符罪帖,基本上包含了世界上所有的語言字符促煮,它也就成為了全世界一種通用的編碼,而且用十六進(jìn)制4位表示一個編碼整袁,非常簡結(jié)直觀菠齿,為大多數(shù)開發(fā)者所接受,特別是十六進(jìn)制編碼后坐昙,可以解決漢字在js再編碼過程中出現(xiàn)亂碼問題绳匀,提高解釋速度,我們建議在js腳本中使用十六進(jìn)制unicode編碼炸客。

UniCode漢字轉(zhuǎn)換襟士,網(wǎng)上很多,但相對比較好使的比較少嚷量,大都寫法一樣乖仇,轉(zhuǎn)換的效果差別不大,或多或少有些遺憾苹支,我這找到個相對較好的赊时,能直接轉(zhuǎn)換標(biāo)點符號的轉(zhuǎn)換器宣渗。

地址:http://javawind.net/tools/native2ascii.jsp?action=transform

另附上Unicode編號表,這下完美了

ASCII碼

在學(xué)校學(xué) C 語言的時候梨州,了解到一些計算機內(nèi)部的機制痕囱,知道所有的信息最終都表示為一個二進(jìn)制的字符串,每一個二進(jìn)制位有 0 和 1 兩種狀態(tài)暴匠,通過不同的排列組合鞍恢,使用 0 和 1 就可以表示世界上所有的東西,感覺有點中國“太極”的感覺——“太極生兩儀每窖,兩儀生四象帮掉,四象生八卦”。

在計算機種中窒典,1 字節(jié)對應(yīng) 8 位二進(jìn)制數(shù)蟆炊,而每位二進(jìn)制數(shù)有 0、1 兩種狀態(tài)瀑志,因此 1 字節(jié)可以組合出 256 種狀態(tài)涩搓。如果這 256 中狀態(tài)每一個都對應(yīng)一個符號,就能通過 1 字節(jié)的數(shù)據(jù)表示 256 個字符劈猪。美國人于是就制定了一套編碼(其實就是個字典)昧甘,描述英語中的字符和這 8 位二進(jìn)制數(shù)的對應(yīng)關(guān)系,這被稱為 ASCII 碼战得。

ASCII 碼一共定義了 128 個字符疾层,例如大寫的字母 A 是 65(這是十進(jìn)制數(shù),對應(yīng)二進(jìn)制是0100 0001)贡避。這 128 個字符只使用了 8 位二進(jìn)制數(shù)中的后面 7 位痛黎,最前面的一位統(tǒng)一規(guī)定為 0。

歷史問題

英語用 128 個字符來編碼完全是足夠的刮吧,但是用來表示其他語言湖饱,128 個字符是遠(yuǎn)遠(yuǎn)不夠的。于是杀捻,一些歐洲的國家就決定井厌,將 ASCII 碼中閑置的最高位利用起來,這樣一來就能表示 256 個字符致讥。但是仅仆,這里又有了一個問題,那就是不同的國家的字符集可能不同垢袱,就算它們都能用 256 個字符表示全墓拜,但是同一個碼
點(也就是 8 位二進(jìn)制數(shù))表示的字符可能可能不同。例如请契,144 在阿拉伯人的 ASCII 碼中是 ?咳榜,而在俄羅斯的 ASCII 碼中是 ?夏醉。

因此,ASCII 碼的問題在于盡管所有人都在 0 - 127 號字符上達(dá)成了一致涌韩,但對于 128 - 255 號字符上卻有很多種不同的解釋畔柔。與此同時,亞洲語言有更多的字符需要被存儲臣樱,一個字節(jié)已經(jīng)不夠用了靶擦。于是,人們開始使用兩個字節(jié)來存儲字符雇毫。

各種各樣的編碼方式成了系統(tǒng)開發(fā)者的噩夢玄捕,因為他們想把軟件賣到國外。于是嘴拢,他們提出了一個“內(nèi)碼表”的概念桩盲,可以切換到相應(yīng)語言的一個內(nèi)碼表寂纪,這樣才能顯示相應(yīng)語言的字母席吴。在這種情況下,如果使用多語種捞蛋,那么就需要頻繁的在內(nèi)碼表內(nèi)進(jìn)行切換孝冒。

Unicode

最終,美國人意識到他們應(yīng)該提出一種標(biāo)準(zhǔn)方案來展示世界上所有語言中的所有字符拟杉,出于這個目的庄涡,Unicode誕生了。

Unicode 當(dāng)然是一本很厚的字典搬设,記錄著世界上所有字符對應(yīng)的一個數(shù)字穴店。具體是怎樣的對應(yīng)關(guān)系,又或者說是如何進(jìn)行劃分的拿穴,就不是我們考慮的問題了泣洞,我們只用知道Unicode 給所有的字符指定了一個數(shù)字用來表示該字符。

對于 Unicode 有一些誤解默色,它僅僅只是一個字符集球凰,規(guī)定了符合對應(yīng)的二進(jìn)制代碼,至于這個二進(jìn)制代碼如何存儲則沒有任何規(guī)定腿宰。它的想法很簡單呕诉,就是為每個字符規(guī)定一個 用來表示該字符的數(shù)字,僅此而已吃度。

Unicode 編碼方案

之前提到甩挫,Unicode 沒有規(guī)定字符對應(yīng)的二進(jìn)制碼如何存儲。以漢字“漢”為例椿每,它的 Unicode 碼點是 0x6c49捶闸,對應(yīng)的二進(jìn)制數(shù)是 110110001001001夜畴,二進(jìn)制數(shù)有 15 位,這也就說明了它至少需要 2 個字節(jié)來表示删壮√盎妫可以想象,在 Unicode 字典中往后的字符可能就需要 3 個字節(jié)或者 4 個字節(jié)央碟,甚至更多字節(jié)來表示了税灌。

這就導(dǎo)致了一些問題,計算機怎么知道你這個 2 個字節(jié)表示的是一個字符亿虽,而不是分別表示兩個字符呢菱涤?這里我們可能會想到,那就取個最大的洛勉,假如 Unicode 中最大的字符用 4 字節(jié)就可以表示了粘秆,那么我們就將所有的字符都用 4 個字節(jié)來表示,不夠的就往前面補 0收毫。這樣確實可以解決編碼問題攻走,但是卻造成了空間的極大浪費,如果是一個英文文檔此再,那文件大小就大出了 3 倍昔搂,這顯然是無法接受的。

于是输拇,為了較好的解決 Unicode 的編碼問題摘符, UTF-8 和 UTF-16 兩種當(dāng)前比較流行的編碼方式誕生了。當(dāng)然還有一個 UTF-32 的編碼方式策吠,也就是上述那種定長編碼逛裤,字符統(tǒng)一使用 4 個字節(jié),雖然看似方便猴抹,但是卻不如另外兩種編碼方式使用廣泛带族。

UTF-8

UTF-8 是一個非常驚艷的編碼方式,漂亮的實現(xiàn)了對 ASCII 碼的向后兼容洽糟,以保證 Unicode 可以被大眾接受炉菲。

UTF-8 是目前互聯(lián)網(wǎng)上使用最廣泛的一種 Unicode 編碼方式,它的最大特點就是可變長坤溃。它可以使用 1 - 4 個字節(jié)表示一個字符拍霜,根據(jù)字符的不同變換長度。編碼規(guī)則如下:

對于單個字節(jié)的字符薪介,第一位設(shè)為 0祠饺,后面的 7 位對應(yīng)這個字符的 Unicode 碼點。因此汁政,對于英文中的 0 - 127 號字符道偷,與 ASCII 碼完全相同缀旁。這意味著 ASCII 碼那個年代的文檔用 UTF-8 編碼打開完全沒有問題。

對于需要使用 N 個字節(jié)來表示的字符(N > 1)勺鸦,第一個字節(jié)的前 N 位都設(shè)為 1并巍,第 N + 1 位設(shè)為0,剩余的 N - 1 個字節(jié)的前兩位都設(shè)位 10换途,剩下的二進(jìn)制位則使用這個字符的 Unicode 碼點來填充懊渡。

編碼規(guī)則如下:

UNICODE 十六進(jìn)制碼點范圍 UTF-8 二進(jìn)制
0000 0000 - 0000 007F 0xxxxxxx
0000 0080 - 0000 07FF 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
根據(jù)上面編碼規(guī)則對照表,進(jìn)行 UTF-8 編碼和解碼就簡單多了军拟。下面以漢字“漢”為利剃执,具體說明如何進(jìn)行 UTF-8 編碼和解碼。

“漢”的 Unicode 碼點是 0x6c49(110 1100 0100 1001)懈息,通過上面的對照表可以發(fā)現(xiàn)肾档,0x0000 6c49 位于第三行的范圍,那么得出其格式為 1110xxxx 10xxxxxx 10xxxxxx辫继。接著怒见,從“漢”的二進(jìn)制數(shù)最后一位開始,從后向前依次填充對應(yīng)格式中的 x骇两,多出的 x 用 0 補上速种。這樣姜盈,就得到了“漢”的 UTF-8 編碼為 11100110 10110001 10001001低千,轉(zhuǎn)換成十六進(jìn)制就是 0xE6 0xB7 0x89。

解碼的過程也十分簡單:如果一個字節(jié)的第一位是 0 馏颂,則說明這個字節(jié)對應(yīng)一個字符示血;如果一個字節(jié)的第一位1,那么連續(xù)有多少個 1救拉,就表示該字符占用多少個字節(jié)难审。

UTF-16

在了解 UTF-16 編碼方式之前,先了解一下另外一個概念——"平面"亿絮。

在上面的介紹中告喊,提到了 Unicode 是一本很厚的字典,她將全世界所有的字符定義在一個集合里派昧。這么多的字符不是一次性定義的黔姜,而是分區(qū)定義。每個區(qū)可以存放 65536 個($2^{16}$)字符蒂萎,稱為一個平面(plane)秆吵。目前,一共有 17 個($2^{5}$)平面五慈,也就是說纳寂,整個 Unicode 字符集的大小現(xiàn)在是 $2^{21}$主穗。

最前面的 65536 個字符位,稱為基本平面(簡稱 BMP )毙芜,它的碼點范圍是從 0 到 $2^{16}-1$忽媒,寫成 16 進(jìn)制就是從 U+0000 到 U+FFFF。所有最常見的字符都放在這個平面腋粥,這是 Unicode 最先定義和公布的一個平面猾浦。剩下的字符都放在輔助平面(簡稱 SMP ),碼點范圍從 U+010000 到 U+10FFFF灯抛。

基本了解了平面的概念后金赦,再說回到 UTF-16。UTF-16 編碼介于 UTF-32 與 UTF-8 之間对嚼,同時結(jié)合了定長和變長兩種編碼方法的特點夹抗。它的編碼規(guī)則很簡單:基本平面的字符占用 2 個字節(jié),輔助平面的字符占用 4 個字節(jié)纵竖。也就是說漠烧,UTF-16 的編碼長度要么是 2 個字節(jié)(U+0000 到 U+FFFF),要么是 4 個字節(jié)(U+010000 到 U+10FFFF)靡砌。那么問題來了已脓,當(dāng)我們遇到兩個字節(jié)時,到底是把這兩個字節(jié)當(dāng)作一個字符還是與后面的兩個字節(jié)一起當(dāng)作一個字符呢通殃?

這里有一個很巧妙的地方度液,在基本平面內(nèi),從 U+D800 到 U+DFFF 是一個空段画舌,即這些碼點不對應(yīng)任何字符堕担。因此,這個空段可以用來映射輔助平面的字符曲聂。

輔助平面的字符位共有 $2^{20}$ 個霹购,因此表示這些字符至少需要 20 個二進(jìn)制位。UTF-16 將這 20 個二進(jìn)制位分成兩半朋腋,前 10 位映射在 U+D800 到 U+DBFF齐疙,稱為高位(H),后 10 位映射在 U+DC00 到 U+DFFF旭咽,稱為低位(L)贞奋。這意味著,一個輔助平面的字符轻专,被拆成兩個基本平面的字符表示忆矛。

因此,當(dāng)我們遇到兩個字節(jié),發(fā)現(xiàn)它的碼點在 U+D800 到 U+DBFF 之間催训,就可以斷定洽议,緊跟在后面的兩個字節(jié)的碼點,應(yīng)該在 U+DC00 到 U+DFFF 之間漫拭,這四個字節(jié)必須放在一起解讀亚兄。

接下來,以漢字"??"為例采驻,說明 UTF-16 編碼方式是如何工作的审胚。

漢字"??"的 Unicode 碼點為 0x20BB7,該碼點顯然超出了基本平面的范圍(0x0000 - 0xFFFF)礼旅,因此需要使用四個字節(jié)表示膳叨。首先用 0x20BB7 - 0x10000 計算出超出的部分,然后將其用 20 個二進(jìn)制位表示(不足前面補 0 )痘系,結(jié)果為0001000010 1110110111菲嘴。接著,將前 10 位映射到 U+D800 到 U+DBFF 之間汰翠,后 10 位映射到 U+DC00 到 U+DFFF 即可龄坪。U+D800 對應(yīng)的二進(jìn)制數(shù)為 1101100000000000,直接填充后面的 10 個二進(jìn)制位即可复唤,得到 1101100001000010健田,轉(zhuǎn)成 16 進(jìn)制數(shù)則為 0xD842。同理可得佛纫,低位為 0xDFB7妓局。因此得出漢字"??"的 UTF-16 編碼為 0xD842 0xDFB7。

Unicode3.0 中給出了輔助平面字符的轉(zhuǎn)換公式:

H = Math.floor((c-0x10000) / 0x400)+0xD800

L = (c - 0x10000) % 0x400 + 0xDC00
根據(jù)編碼公式雳旅,可以很方便的計算出字符的 UTF-16 編碼跟磨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末间聊,一起剝皮案震驚了整個濱河市攒盈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哎榴,老刑警劉巖型豁,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尚蝌,居然都是意外死亡迎变,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門飘言,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衣形,“玉大人,你說我怎么就攤上這事∽晃猓” “怎么了倒源?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長句狼。 經(jīng)常有香客問我笋熬,道長,這世上最難降的妖魔是什么腻菇? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任胳螟,我火速辦了婚禮,結(jié)果婚禮上筹吐,老公的妹妹穿的比我還像新娘糖耸。我一直安慰自己,他們只是感情好丘薛,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布蔬捷。 她就那樣靜靜地躺著,像睡著了一般榔袋。 火紅的嫁衣襯著肌膚如雪周拐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天凰兑,我揣著相機與錄音妥粟,去河邊找鬼。 笑死吏够,一個胖子當(dāng)著我的面吹牛勾给,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锅知,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼播急,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了售睹?” 一聲冷哼從身側(cè)響起桩警,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昌妹,沒想到半個月后捶枢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡飞崖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年烂叔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片固歪。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒜鸡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逢防,我是刑警寧澤康聂,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胞四,受9級特大地震影響恬汁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辜伟,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一氓侧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧导狡,春花似錦约巷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枚赡,卻和暖如春氓癌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贫橙。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工贪婉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卢肃。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓疲迂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親莫湘。 傳聞我的和親對象是個殘疾皇子尤蒿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容