徹底搞明白 Gb2312、Gbk 和 Gb18030
本文轉(zhuǎn)載自微信公眾號「Linux開發(fā)那些事兒」,作者LinuxThings。轉(zhuǎn)載本文請聯(lián)系Linux開發(fā)那些事兒公眾號。
日常工作的過程中,關(guān)于字符編碼的問題經(jīng)常讓人頭疼不已,這篇文章就來捋一捋關(guān)于 GB2312、GBK、GB18030 相關(guān)的知識 以及它們和 Unicode 的關(guān)系。
簡介
- GB2312
1980 年,中國發(fā)布了第一個漢字編碼標(biāo)準(zhǔn),也即 GB2312 ,全稱 《信息交換用漢字編碼字符集·基本集》,通常簡稱 GB (“國標(biāo)”漢語拼音首字母), 共收錄了 6763 個常用的漢字和字符,此標(biāo)準(zhǔn)于次年5月實施,它滿足了日常 99% 漢字的使用需求
- GBK
由于有些漢字是在 GB2312 標(biāo)準(zhǔn)發(fā)布之后才簡化的,還有一些人名、繁體字、日語和朝鮮語中的漢字也沒有包括在內(nèi),所以,在 GB2312 的基礎(chǔ)上添加了這部分字符,就形成了 GBK ,全稱 《漢字內(nèi)碼擴展規(guī)范》,共收錄了兩萬多個漢字和字符,它完全兼容 GB2312
GBK 于 1995 年發(fā)布,不過它只是 "技術(shù)規(guī)范指導(dǎo)性文件",并不屬于國家標(biāo)準(zhǔn)
- GB18030
GB18030 全稱《信息技術(shù) 中文編碼字符集》 ,共收錄七萬多個漢字和字符, 它在 GBK 的基礎(chǔ)上增加了中日韓語中的漢字 和 少數(shù)民族的文字及字符,完全兼容 GB2312,基本兼容 GBK
GB18030 發(fā)布過兩個版本,第一版于 2000 年發(fā)布,稱為 GB18030-2000,第二版于 2005 年發(fā)布,稱為 GB18030-2005
編碼方式
ASICII、GB2312、GBK、GB18030 之間的關(guān)系可以用下圖表示
GB2312 兼容 ASICII 編碼, GBK 兼容 GB2312 編碼,GB18030 兼容 GB2312 編碼 和 GBK 編碼
實際生活中,我們用到的 99% 的漢字,都屬于 GB2312 編碼范圍 ,GB2312 每個編碼對應(yīng)的是哪個漢字可以參考 GB2312簡體中文編碼表, GBK 編碼可以參考 GBK編碼表, GB18030 可以參考 GB18030-2005 文檔
GB2312 編碼
GB2312 把每個漢字都編碼成兩個字節(jié),第一個字節(jié)是高位字節(jié),第二個字節(jié)是低位字節(jié)
GB2312 為了兼容 ASICII ,其編碼需要進(jìn)行一些轉(zhuǎn)換才能避免和 ASICII 編碼重疊,轉(zhuǎn)換的過程涉及到區(qū)位碼和國標(biāo)碼的概念,下面說明轉(zhuǎn)成內(nèi)碼的過程
- 區(qū)位碼
GB2312 對漢字進(jìn)行了分區(qū)處理,每個區(qū)含有 94 個漢字或者字符,總共有 94 個區(qū),每個漢字或者字符都對應(yīng)一個 分區(qū)編號和分區(qū)內(nèi)的位置編號,稱為 區(qū)位碼
比如:漢字 "中" 字的 分區(qū)編號是 54,分區(qū)內(nèi)位置編號是 48,所以,"中" 字的區(qū)位碼是 54 48
- 國標(biāo)碼
國標(biāo)碼 也叫 交換碼,用于交換文件所使用的編碼,在早期,不同的操作系統(tǒng)可能使用不同的內(nèi)碼,如果它們之間要交換文件,則會發(fā)生亂碼的現(xiàn)象,當(dāng)時的解決方法是交換文件之前先轉(zhuǎn)成交換碼再交換,接收者收到之后再轉(zhuǎn)成內(nèi)碼
交換碼是比較早期的一種方案,目前系統(tǒng)大都采用內(nèi)碼作為交換碼
ASICII 碼為 0- 31 的這 32 個字符是不可顯示的字符,為了避免和這些字符的碼點沖突,將 分區(qū)編號和分區(qū)內(nèi)位置編號都加上 32 ,把這個轉(zhuǎn)換的結(jié)果稱為 國標(biāo)碼
比如:漢字 "中" 字分區(qū)編號是 54,分區(qū)內(nèi)位置編號是 48,加上 32 之后,分區(qū)編號是 54 + 32 = 86, ,分區(qū)內(nèi)位置編號是 48 + 32 = 80,所以 "中" 字 的國標(biāo)碼是 86 80
- 內(nèi)碼
國標(biāo)碼 和 ASICII 碼還是存在一定的重復(fù),比如 "中" 字 的國標(biāo)碼是 86 80,對應(yīng)第一個字節(jié)是 86,第二個字節(jié)是 80,而在 ASICII 碼中它們分別代表大寫字母V 和 大寫字母 P,這就無法區(qū)分它們到底是一個漢字,還是兩個字母
為了解決這一點,把國標(biāo)碼中的每個字節(jié)的最高位置為 1,也即相當(dāng)于每個字節(jié)都加上 128 ( 2的7次方 ),還是以 "中" 字為例,它的 國標(biāo)碼是 86 80,加上 128 后, 第一個字節(jié)是 86 + 128 = 214, 第二個字節(jié)是 80 + 128 = 208,轉(zhuǎn)化成 16 進(jìn)制是 0xD6 0xD0 ( 214 的十六進(jìn)制是 0xD6, 208 的十六進(jìn)制是 0xD0 )
國標(biāo)碼的每個字節(jié)都加上 128 后,得到國標(biāo)碼的機內(nèi)碼,簡稱 內(nèi)碼,漢字是以內(nèi)碼的形式在計算機中存儲和傳播的
上面介紹 區(qū)位碼 和 國標(biāo)碼,主要是是為了說明 漢字內(nèi)碼是如何一步一步發(fā)展而來的
可以看出,漢字的 區(qū)位碼 + 32 + 128 就得到了內(nèi)碼,進(jìn)一步簡化,區(qū)位碼 + 32 + 128 = 區(qū)位碼 + 160 = 區(qū)位碼 + 0xA0(128 的十六進(jìn)制) , 因此 內(nèi)碼 = 區(qū)位碼 + 0xA0
比如:"中" 字的區(qū)位碼是 54 48,對應(yīng)的十六進(jìn)制是0x36 0x30,因此它的內(nèi)碼為 (0x36 + 0xA0) (0x30 + 0xA0),也即 0xD6 0xD0
關(guān)于漢字的區(qū)位碼請參考:漢字區(qū)位碼
GB2312 有效的編碼范圍如下圖所示
上圖中 紅色欄 表示 ASICII 的編碼范圍,綠色欄表示 GB2312 編碼范圍
GBK 編碼
和 GB2312 一樣,GBK 也是雙字節(jié)編碼,為了向下兼容 GB2312, GBK 使用了 GB2312 沒有用到的編碼區(qū)域,總的編碼范圍是: 第一個字節(jié) 0x81–0xFE,第二個字節(jié) 0x40–0xFE, 具體的編碼范圍細(xì)分如下
上述表格中,紅色欄是 GBK 中包含的 GB2312 以及 ASICII 的編碼范圍,它們的編碼范圍保持不變
綠色欄的是 GBK 新增的編碼范圍
紫色欄是 用戶自定義編碼范圍
GB18030 編碼
與 GBK 不同的是,GB18030 是變長多字節(jié)字符集,每個字或字符可以由一個,兩個或四個字節(jié)組成,所以它的編碼空間是很大的,最多可以容納 161 萬個字符
由于需要兼容 GBK,四個字節(jié)的前兩個字節(jié)和 GBK 編碼保持一致,GB18030 具體的編碼范圍如下
GB18030 與 Unicode
GB18030 和 Unicode 相當(dāng)于兩套單獨的編碼體系,它們都對世界上大部分字符進(jìn)行編碼,賦予每個字符一個唯一的編號,只不過對于同一個字符,GB18030 和 Unicode 對應(yīng)的編號是不一樣的, 比如:漢字 "中" 字的 GB18030 編碼是 0xD6D0, 對應(yīng)的 Unicode 碼元是 0x4E2D, 從這一點上可以認(rèn)為 GB18030 是一種 Unicode 的轉(zhuǎn)換格式
注意:要表達(dá) Unicode 的編碼格式才真正算得上 Unicode 轉(zhuǎn)換格式,所以嚴(yán)格意義上說 GB18030 并不是真正的 Unicode 轉(zhuǎn)換格式
GB18030 既是字符集又是編碼格式,也即字符在字符集中的編號以及存儲是進(jìn)行編碼用的編號是完全相同的,而 Unicode 僅僅是字符集,它只規(guī)定了字符的唯一編號,它的存儲是用其他的編碼格式的,比如 UTF8、UTF16 等等
既然 GB18030 和 Unicode 都能表示世界上大部分字符,為什么要弄兩套字符集呢,一套的話不更有利于信息的傳播嗎?
1、在 Unicode 出現(xiàn)之前,沒有統(tǒng)一的字符編碼,每個操作系統(tǒng)上都有自己的一套編碼標(biāo)準(zhǔn),像早期的 window 上需要安裝字符集,才能支持中文,這里的字符集就是微軟自定的標(biāo)準(zhǔn),換個其他系統(tǒng)就會失效
2、對于大部分中文字符來說,采用 GB18030 編碼的話,只需兩個字節(jié),如果采用 UTF8 編碼,就需要三個字節(jié), 所以用 GB18030 存儲和傳輸更節(jié)省空間
ASICII、GB2312、GBK、GB18030 以及 UTF8 的關(guān)系
它們的關(guān)系如下圖
由上圖可知,GB2312、GBK、GB18030 以及 UTF8 共同點是都兼容 ASICII
全角和半角字符
使用輸入法輸入字符的時候,有全角和半角之分,對于同一個字符,全角和半角對應(yīng)的碼點是不一樣的
下面列出了一些字符的全角和半角的外觀截圖
半角是 ASICII 碼中的字符,對應(yīng)的編碼范圍是 0x00 - 0x7F,每個字符占一個字節(jié)
全角是 GB2312 中的字符,每個字符占用兩個字節(jié) ,其編碼范圍和半角字符范圍是沒有重疊的
對于漢字來說,是沒有全角和半角之分的
比如:上圖中 @ 符號,全角的編碼是 0xA3C0,占兩個字節(jié), 半角的編碼是 0x40,占一個字節(jié)
通過內(nèi)碼輸入漢字的方法
有時需要輸入一些特殊的字符,比如 帶圓圈的數(shù)字字符 ①,一般需要借助輸入法的小鍵盤來輸入
這里介紹一種使用字符內(nèi)碼來快速輸入的方法: 按住 Alt 鍵不放,輸入字符內(nèi)碼的十進(jìn)制,輸入完后松開 Alt 鍵
例如,輸入帶圓圈的數(shù)字 ② 的步驟
1、查找?guī)A圈數(shù)字 2 的字符的編碼 0xA2DA,十進(jìn)制是 41690
2、按住 Alt 鍵不放,輸入 41690,松開 Alt 鍵
3、輸入完畢,這時就會出現(xiàn)字符 ②
一般 Linux的 SSH 連接工具 或 windows 上的記事本,都支持內(nèi)碼輸入