字符编码(GB 2312、GBK、UTF-8、UTF-16)
GB 2312
GB 2312 是 1980 年發(fā)布的中文編碼,共收錄 7445 個字符,有 6763 個漢字以及 682 個非漢字字符,其中一級漢字 3755 個,二級漢字 3008 個。
GB 2312 采用雙字節(jié)編碼,兩字節(jié)最高位均為 1,所以可以兼容 ASCII 碼。
整個字符集分為 94 個區(qū),每個區(qū)有 94 個位,7445 個字符被填入到這 94*94 個區(qū)位中。
每個區(qū)位上只有一個字符,因此可用所在的區(qū)和位來對漢字進(jìn)行編碼,稱為區(qū)位碼。區(qū)位碼加上 0x2020 就得到國標(biāo)碼。國標(biāo)碼再加上 0x8080 就得到了兩字節(jié)的計算機(jī)內(nèi)碼。
1 和 94 對應(yīng)的 16 進(jìn)制分別為 0x01 和 0x5E。
所以區(qū)位碼的范圍是:0x0101~0x5E5E,加上 0x2020
得到國標(biāo)碼的范圍是:0x2121~0x7E7E,加上 0x8080
得到內(nèi)碼的范圍是:0xA1A1~0xFEFE
在 GB 2321 中:
1 到 9 區(qū),為非漢字字符。
10 到 15 區(qū),沒有字符。
16 到 55 區(qū),為一級漢字,按拼音排序。
56 到 87 區(qū),為二級漢字,按部首/筆畫排序。
88 到 94 區(qū),沒有字符。
所以對應(yīng)到內(nèi)碼:
0xA1A1~0xA9FE 為非漢字字符。
0xAAA1~0xAFFE 無編碼。
0xB0A1~0xF7FE 為漢字。
0xF8A1~0xFEFE 無編碼。
GB 2312 編碼表參考:
http://doc.chacuo.net/gb2312
GBK
GBK 即漢字內(nèi)碼擴(kuò)展規(guī)范,共收錄 21886 個漢字和非漢字字符。
兼容 GB 2312,同樣采樣雙字節(jié)編碼,同樣兼容 ASCII 碼,與 GB 2312 不同的是,它只要求第一個字節(jié)最高位為 1。 解碼時,遇到首位為 1 的字節(jié),就連同下一個字節(jié)一起表示一個 GBK 編碼,遇到首位為 0 的字節(jié),就直接按 ASCII 碼處理。
GBK 編碼范圍為 0x8140~0xFEFE,第一個字節(jié)在 0x81~0xFE 之間,第二個字節(jié)在 0x40~0xFE 之間,剔除了第二個字節(jié)為 0x7F 的編碼。
GBK 編碼表參考:
http://doc.chacuo.net/gbk
GB 18030(較少用)
GB 18030 兼容 GBK 和 ASCII,共收錄漢字 70244 個,采用一二四字節(jié)可變長編碼。
Unicode
Unicode 是一個標(biāo)準(zhǔn),定義了一個字符集以及這個字符集對應(yīng)的一系列編碼方案,即 Unicode 字符集和 UTF-8、UTF-16、UTF-32 等等編碼。
通常我們說的 Unicode 僅僅指的是 Unicode 字符集,這個字符集的目的是收錄全世界的所有字符,為每一個字符分配一個唯一的數(shù)字編號,這個數(shù)字編號用 Unicode 定義的術(shù)語來說就是 code point (譯作碼點(diǎn)或碼位)。
Unicode 的碼點(diǎn)記作 U+[XX]XXXX,X 表示16 進(jìn)制數(shù)。碼點(diǎn)的范圍是 U+0000~U+10 FFFF。
類似于 GB 2312 按區(qū)劃分字符集,Unicode 按平面(plane)劃分字符集,將字符集劃分為 17 個平面(plane 0 到 plane 16),每個平面包含 65536 個碼點(diǎn),碼點(diǎn)范圍是 U+0000~U+FFFF。其中,第一個平面,即 plane 0,又叫做 BMP(Basic Multilingual Plane,基本多語言平面),包含了世界上日常使用的絕大部分字符。
Unicode 字符集參考:
https://unicode-table.com/cn/
UTF-8、UTF-16、UTF-32
UTF 是 Unicode Transformation Format 的縮寫,規(guī)定了 Unicode 碼點(diǎn)轉(zhuǎn)換為計算機(jī)內(nèi)碼的規(guī)則。
UTF-32 最簡單粗暴,直接使用碼點(diǎn)作為四字節(jié)內(nèi)碼。比較浪費(fèi)空間,不是很常用。
考慮到平時使用的大多數(shù)字符都在 BMP(U+0000~U+FFFF)里,只需要使用兩個字節(jié)編碼即可。萬一用到 BMP 以外的字符,再使用四個字節(jié)編碼。這就是UTF-16。Java8 內(nèi)部就是使用 UTF-16 編碼字符串。
由于 UTF-16 和 UTF-32 都是一次讀取 2 字節(jié)或 4字節(jié),這樣一是不兼容 ASCII 碼,二是在有大小端之分的機(jī)器間傳輸時,需要考慮字節(jié)序的問題,否則會造成亂碼。
UTF-8 不同于 UTF-16、UTF-32,能夠兼容 ASCII 碼,是一種變長字節(jié)的編碼方式,每次讀取一個字節(jié),所以無需考慮字節(jié)序。
Unicode 標(biāo)準(zhǔn)提出使用 BOM(Byte Order Mark)來標(biāo)識字節(jié)序。做法是在文件開頭加上 “ZERO WIDTH NO-BREAK SPACE” 這個字符,這是一個零寬度不換行空格,是一個不可見字符,對應(yīng) Unicode 碼點(diǎn)為 U+FEFF。
按理說 UTF-8 是不需要 BOM 的,但是為了在解碼時明確區(qū)分 UTF-8 和其它編碼,也可以加上 BOM,Unicode 標(biāo)準(zhǔn)也允許這么做。微軟的很多軟件就會默認(rèn)在 UTF-8 編碼的文件中加上 BOM,不過有時候,這種做法會造成很多不必要的麻煩。
下表是各種 UTF 的 BOM:
| UTF-8 without BOM | 無 |
| UTF-8 with BOM | EF BB BF |
| UTF-16LE | FF FE |
| UTF-16BE | FE FF |
| UTF-32LE | FF FE 00 00 |
| UTF-32BE | 00 00 FE FF |
UTF-8 編碼規(guī)則
UTF-8 使用 1 至 4 個字節(jié)為每個字符編碼:
- Unicode 碼點(diǎn)在 U+0000~U+007F 之間的 ASCII 碼字符,直接使用 1 字節(jié)編碼;
- Unicode 碼點(diǎn)在 U+0080~U+07FF 之間的字符,包括帶有變音符號的拉丁文、希臘文、西里爾字母、亞美尼亞語、希伯來文、阿拉伯文、敘利亞文等,使用 2 字節(jié)編碼;
- 其他語言的常用字符(包括中日韓文字、東南亞文字、中東文字等),使用 3 字節(jié)編碼;
- 其他極少使用的語言字符,使用 4 字節(jié)編碼;
UTF-8 編碼規(guī)則表:
| U+0000~U+007F | 7 | 0xxx xxxx | 1 |
| U+0080~U+07FF | 11 | 110x xxxx 10xx xxxx | 2 |
| U+0800~U+FFFF | 16 | 1110 xxxx 10xx xxxx 10xx xxxx | 3 |
| U+1 0000~U+1F FFFF | 21 | 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx | 4 |
以漢字“碼”舉例,說明 UTF-8 的編碼過程:
碼
Unicode 碼點(diǎn)?U+7801
對應(yīng)二進(jìn)制?0111 1000 0000 0001
分為三部分?0111、1000 00、00 0001
UTF-8 模板?1110 xxxx 10xx xxxx 10xx xxxx
填充對應(yīng)位置?1110 0111 1010 0000 1000 0001
編碼結(jié)果?0xE7A081
UTF-16 編碼規(guī)則
UTF-16 使用 2 或 4 個字節(jié)為每個字符編碼:
- Unicode 碼點(diǎn)在 U+0000~U+FFFF 之間的字符,直接使用 2 字節(jié)編碼。
- Unicode 碼點(diǎn)在 U+10000~U+10FFFF 之間的字符,使用 4 字節(jié)編碼。具體來說就是:將碼點(diǎn)減去 0x10000,然后把結(jié)果對應(yīng)的二進(jìn)制數(shù)分為兩部分,高位部分用一個介于 0xD800~0xDBFF 之間的雙字節(jié)存儲,低位部分用一個介于 0xDC00~0xDFFF 之間的雙字節(jié)存儲。
UTF-16 編碼規(guī)則表:
| U+0000~U+FFFF | 16 | xxxx xxxx xxxx xxxx | 2 |
| U+1 0000~U+10 FFFF | 20 | 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx | 4 |
2 字節(jié)編碼直接使用碼點(diǎn)沒什么好說的,下面舉個例子說明一下 4 字節(jié)編碼:
Unicode碼點(diǎn)?U+20000
減去0x10000?0x10000
對應(yīng)二進(jìn)制?0001 0000 0000 0000 0000
分成兩部分?0001 0000 00、00 0000 0000
UTF-16 模板?1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
填充對應(yīng)位置?1101 1000 0100 0000 1101 1100 0000 0000
編碼結(jié)果?0xD840DC00
位于 U+D800~U+DFFF 之間的 Unicode 碼點(diǎn)是特別為四字節(jié)的 UTF-16 編碼預(yù)留的,在這個范圍內(nèi)的碼點(diǎn)沒有指定任何字符。
記事本輸入“聯(lián)通”保存,再次打開時亂碼的問題:
簡單來講就是,windows 自帶的記事本,輸入”聯(lián)通“后,默認(rèn)按照 ANSI 編碼保存,ANSI 編碼在中國指的就是 GBK 編碼。而打開時,記事本發(fā)現(xiàn)文件也符合 UTF-8 的編碼格式,便優(yōu)先使用 UTF-8 解碼,從而導(dǎo)致亂碼。
具體的,“聯(lián)通”的 GBK 編碼為 0xC1AA 0xCDA8,都符合 UTF-8 的雙字節(jié)編碼格式(110x xxxx 10xx xxxx)。
參考:
https://baike.baidu.com/item/%E4%BF%A1%E6%81%AF%E4%BA%A4%E6%8D%A2%E7%94%A8%E6%B1%89%E5%AD%97%E7%BC%96%E7%A0%81%E5%AD%97%E7%AC%A6%E9%9B%86/8074272?fromtitle=GB2312&fromid=483170&fr=aladdin
https://baike.baidu.com/item/Unicode/750500?fr=aladdin#4_1
https://baike.baidu.com/item/UTF-8/481798?fr=aladdin
https://www.zhihu.com/question/23374078/answer/24385963
https://zhuanlan.zhihu.com/p/26261762?utm_source=qq&utm_medium=social&utm_oi=934366346112811008
https://blog.csdn.net/guxiaonuan/article/details/78678043#commentBox
總結(jié)
以上是生活随笔為你收集整理的字符编码(GB 2312、GBK、UTF-8、UTF-16)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 符合语言习惯的Python优雅编程技巧
- 下一篇: 程序员看了表示很开心