unicode筆記

標簽(空格分隔): unicode


  • 解釋
      Unicode是一種字符編碼方法,是由國際組織設(shè)計,目的是容納全世界所有語言文字的編碼方案。Unicode的學(xué)名是"Universal Multiple-Octet Coded Character Set",簡稱為UCS。UCS可以看作是"Unicode Character Set"的縮寫。
      任何文字在Unicode中都對應(yīng)一個值,這個值稱為代碼點(code point)。
      Unicode 的實現(xiàn)方式稱為 Unicode轉(zhuǎn)換格式(Unicode Transformation Format,簡稱為 UTF)。unicode主要有三種編碼實現(xiàn):一類 32 位形式(UTF-32)、一類 16 位形式(UTF-16)和一類 8 位形式(UTF-8)。

  • ASCII 編碼

ASCII(American Standard Code for Information Interchange,美國信息互換標準代碼),是基于拉丁字母的一套電腦編碼系統(tǒng),1967年美國首次公布編碼標準,它主要用于顯示現(xiàn)代英語和其他西歐語言。它是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng),并等同于國際標準ISO/IEC 646。

ASCII 碼一共規(guī)定了128個字符的編碼,比如空格SPACE是32(二進制00100000),大寫的字母A是65(二進制01000001)。這128個符號,只占用了一個字節(jié)的后面7位,最前面的一位統(tǒng)一規(guī)定為0。
  ASCII 字符集由95個可打印字符(0x20-0x7E)和33個控制字符(0x00-0x1F,0x7F)組成。可打印字符用于顯示在輸出設(shè)備上,例如熒屏或者打印紙上,控制字符用于向計算機發(fā)出一些特殊指令,例如0x07會讓計算機發(fā)出嗶的一聲,0x00通常用于指示字符串的結(jié)束,0x0D和0x0A用于指示打印機的打印針頭退到行首(回車)并移到下一行(換行).

  • latin1 編碼
      Latin1是ISO-8859-1的別名,有些環(huán)境下寫作Latin-1。ISO-8859-1編碼是單字節(jié)編碼,向下兼容ASCII,其編碼范圍是 0x00-0xFF,0x00-0x7F之間完全和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號。

  • UCS-2、UCS-4、BMP
      UCS有兩種格式:UCS-2和UCS-4,都是固定字節(jié)編碼。顧名思義,UCS-2就是用2個字節(jié)編碼,UCS-4就是用4個字節(jié)(實際上只用了31位,最高位必須為0)編碼?;径嗾Z言平面內(nèi),從U+D800到U+DFFF之間的碼位區(qū)段是永久保留不映射到Unicode字符
      UCS-2有216=65536個碼位(為了兼容Unicode,0xD800-0xDFFF之間的碼位未使用),UCS-4有231=2147483648個碼位。
      UCS-4根據(jù)最高位為0的最高字節(jié)分成2^7=128個group。每個group再根據(jù)次高字節(jié)分為256個plane。每個plane根據(jù)第3個字節(jié)分為256行 (rows),每行包含256個cells。當(dāng)然同一行的cells只是最后一個字節(jié)不同,其余都相同。
      Unicode編碼點分為17個平面(plane),每個平面包含2^16(即65536)個碼位(code point),而第一個平面稱為“基本多語言平面”(Basic Multilingual Plane,簡稱BMP),其余平面稱為“輔助平面”(Supplementary Planes)。17個平面的碼位可表示為從U+xx0000到U+xxFFFF,其中xx表示十六進制值從0x00到0x10,共計17個平面。其中BMP(0x00000xFFFF)中0xD8000xDFFF之間的碼位作為保留,未使用。
      UCS-2用2個字節(jié)編碼,只能編碼BMP中的字符。此時UTF-16與UCS-2的編碼一樣(都直接使用Unicode的碼位作為編碼值)。
      將UCS-4的BMP去掉前面的兩個零字節(jié)就得到了UCS-2。在UCS-2的兩個字節(jié)前加上兩個零字節(jié),就得到了UCS-4的BMP。而目前的UCS-4規(guī)范中還沒有任何字符被分配在BMP之外。

  • UTF-16

UTF-16是字符編碼表(Character Encoding Form,也稱為 "storage format")的一種實現(xiàn)方式。即把Unicode字符集的抽象碼位映射為16位長的整數(shù)(即碼元, 長度為2 Byte)的序列,用于數(shù)據(jù)存儲或傳遞。Unicode字符的碼位,需要1個或者2個16位長的碼元來表示,因此這是一個變長表示。

UTF-16可以表示所有的unicode編碼點的字符,即0x000000 ~ 0x10FFFF的所有字符,對于BMP的字符,采用一個碼元表示,對于從0x010000~0x10FFFF的字符,則采用2個碼元表示。具體的表示如下表:

Unicode范圍 UTF-16編碼方式
U+0000 ~ U+FFFF 2 Byte存儲,編碼后等于Unicode值
U+10000 ~ U+10FFFF 4 Byte存儲,現(xiàn)將Unicode值減去(0x10000),得到20bit長的值。再將Unicode分為高10位和低10位。UTF-16編碼的高位是2 Byte,高10位Unicode范圍為0-0x3FF,將Unicode值加上0XD800,得到高位代理(或稱為前導(dǎo)代理,存儲高位);低位也是2 Byte,低十位Unicode范圍一樣為0~0x3FF,將Unicode值加上0xDC00,得到低位代理(或稱為后尾代理,存儲低位)

根據(jù)上面的轉(zhuǎn)換方式,我們就能夠?qū)nicode碼根據(jù)UTF-16的編碼方式進行轉(zhuǎn)換。下面我們?nèi)匀煌ㄟ^兩個例子來看下:
  U+0020,這個值的范圍在第一部分,即經(jīng)過UTF-16編碼后,結(jié)果仍然為U+0020,在內(nèi)存中的順序為00 20。
  U+12345, 這個值的范圍在第二部分,因此需要先減去0x10000,得到0x02345,二進制表示為 0000 0010 0011 0100 0101,拆分成高10位00 0000 1000(0x0008)和低10位11 0100 0101(0x0345)。根據(jù)上面規(guī)則加上特定值后,高位代理值為 0x0008 + 0xD800 = 0xD808,低位代理值為0x0345 + 0xDC00 = 0xDF45,最終內(nèi)存中的順序為D8 08 DF 45。

  • UTF-32

UTF-32是字符編碼表(Character Encoding Form,也稱為 "storage format")的一種實現(xiàn)方式,固定用4個字節(jié)表示Unicode的編碼字符。
  UTF-32直接用4個字節(jié)存儲unicode編碼字符,因此并不需要對編碼進行算法轉(zhuǎn)換,并且可以表示所有UCS-4碼位。

  • UTF-8

UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼,又稱萬國碼。由Ken Thompson于1992年創(chuàng)建?,F(xiàn)在已經(jīng)標準化為RFC 3629。UTF-8用1到6個字節(jié)編碼Unicode字符。用在網(wǎng)頁上可以統(tǒng)一頁面顯示中文簡體繁體及其它語言(如英文,日文,韓文)。

UTF-8對于所有的unicode編碼點的字符,即0x000000 ~ 0x10FFFF的所有字符,需要14個字節(jié)編碼,也就是常說的UTF-8的字節(jié)范圍為14;UTF-8可表示所有的UCS-4碼位,這時需要1~6個字節(jié)。
  
UTF-8的編碼規(guī)則很簡單,只有二條:
  1. 對于單字節(jié)的符號,字節(jié)的第一位設(shè)為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。
  2. 對于n字節(jié)的符號(n>1),第一個字節(jié)的前n位都設(shè)為1,第n+1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。
  下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。

Unicode符號范圍(十六進制) UTF-8編碼方式(二進制)
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ù)上表,解讀 UTF-8 編碼非常簡單。如果一個字節(jié)的第一位是0,則這個字節(jié)單獨就是一個字符;如果第一位是1,則連續(xù)有多少個1,就表示當(dāng)前字符占用多少個字節(jié)。
  下面,以漢字嚴為例,演示如何實現(xiàn) UTF-8 編碼。
  嚴的 Unicode 是4E25(0100 1110 0010 0101),根據(jù)上表,可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800 - 0000 FFFF),因此嚴的 UTF-8 編碼需要三個字節(jié),即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,從嚴的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。將嚴的二進制數(shù) 0100 1110 0010 0101 劃分成 0100 111000 100101 這樣就得到了,嚴的 UTF-8 編碼是 11100100 10111000 10100101,轉(zhuǎn)換成十六進制就是E4B8A5

128 個 ASCII 字符(Unicode 范圍由 U+0000 至 U+007F)只需一個字節(jié),帶有變音符號的拉丁文、希臘文、西里爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及馬爾代夫語(Unicode 范圍由 U+0080 至 U+07FF)需要二個字節(jié),其他基本多文種平面(BMP)中的字符(CJK(中日韓統(tǒng)一表意文字)屬于此類)使用三個字節(jié),其他 Unicode 輔助平面的字符使用四字節(jié)編碼。
  
  占2個字節(jié)的:帶有附加符號的拉丁文、希臘文、西里爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文及它拿字母則需要二個字節(jié)編碼
  占3個字節(jié)的:基本等同于GBK,含21000多個漢字
  占4個字節(jié)的:中日韓超大字符集里面的漢字,有5萬多個
  一個utf8數(shù)字占1個字節(jié)
  一個utf8英文字母占1個字節(jié)
  少數(shù)漢字是每個占用3個字節(jié),多數(shù)占用4個字節(jié)。根據(jù)維基百科中日韓統(tǒng)一表意文字的統(tǒng)計,至2020年,漢字unicode編碼中總共收錄92857個漢字,其中位于BMP的共有27594個漢字。
  
  占用3個字節(jié)的范圍

U+2E80 - U+2EF3 : 0xE2 0xBA 0x80 - 0xE2 0xBB 0xB3 共 115 個
U+2F00 - U+2FD5 : 0xE2 0xBC 0x80 - 0xE2 0xBF 0x95 共 213 個
U+3005 - U+3029 : 0xE3 0x80 0x85 - 0xE3 0x80 0xA9 共 36 個
U+3038 - U+4DB5 : 0xE3 0x80 0xB8 - 0xE4 0xB6 0xB5 共 7549 個
U+4E00 - U+FA6A : 0xE4 0xB8 0x80 - 0xEF 0xA9 0xAA 共 44138 個
U+FA70 - U+FAD9 : 0xEF 0xA9 0xB0 - 0xEF 0xAB 0x99 共 105 個

合計: 52156 個

占用4個字節(jié)的范圍

U+20000 - U+2FA1D : 0xF0 0xA0 0x80 0x80 - 0xF0 0xAF 0xA8 0x9D 共 64029 個

合計: 64029 個

  • Little endian 和 Big endian

big endian和little endian是CPU處理多字節(jié)數(shù)的不同方式。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時,究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是big endian。還是將49寫在前面,就是little endian。
  “endian”這個詞出自《格列佛游記》。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,由此曾發(fā)生過六次叛亂,其中一個皇帝送了命,另一個丟了王位。
  我們一般將endian翻譯成“字節(jié)序”,將big endian和little endian稱作“大尾”和“小尾”。

  • UTF的字節(jié)序和BOM

UTF-8以字節(jié)為編碼單元,沒有字節(jié)序的問題。UTF-16以兩個字節(jié)為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節(jié)序。例如收到一個“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節(jié)流“594E”,那么這是“奎”還是“乙”?
  Unicode規(guī)范中推薦的標記字節(jié)順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:
  在UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應(yīng)該出現(xiàn)在實際傳輸中。UCS規(guī)范建議我們在傳輸字節(jié)流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。
  這樣如果接收者收到FEFF,就表明這個字節(jié)流是Big-Endian的;如果收到FFFE,就表明這個字節(jié)流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。
  UTF-8不需要BOM來表明字節(jié)順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。所以如果接收者收到以EF BB BF開頭的字節(jié)流,就知道這是UTF-8編碼了。
  Windows就是使用BOM來標記文本文件的編碼方式的。
  對于UTF-16編碼,big endian的寫法是加FE FF,little endian 的寫法是加FF FE;而對于UTF-32編碼,big endian的寫法是加 00 00 FE FF,little endian 的寫法是加FF FE 00 00 。

參考資料

  1. Unicode中UTF-8與UTF-16編碼詳解
  2. 程序員趣味讀物:談?wù)刄nicode編碼
  3. 字符編碼筆記:ASCII,Unicode 和 UTF-8
  4. UTF-8編碼占幾個字節(jié)?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容