1. ASCII
??我們知道,計算機內(nèi)部是通過二進制數(shù)據(jù)進行操作的旬牲,所有的信息最終都會轉(zhuǎn)換為一個二進制值仿粹,二進制只有0和1兩種狀態(tài),因此8個二進制位就可以組合出256種狀態(tài)原茅,而8個二進制位被稱為一個字節(jié)(byte)吭历,也就是說一個字節(jié)可以用來表示256種狀態(tài),狀態(tài)的范圍是0000 0000
到1111 1111
擂橘。
??最開始計算機剛出現(xiàn)的時候毒涧,計算機的發(fā)源地,也就是美國的一群人贝室,通過編碼的方式將所有的英文及相應(yīng)的標(biāo)點符號契讲,空格,數(shù)字等滑频,編到了1個字節(jié)的后7位上捡偏,也就是一個字節(jié)的0-127種狀態(tài),范圍是
0000 0000
到0111 1111
峡迷,這樣當(dāng)計算機讀到對應(yīng)的二進制值時银伟,就可以解析為相應(yīng)的符號你虹,然后進行后續(xù)的操作,而這種編碼方式被稱為ASCII
編碼彤避,所以ASCII碼的編碼形式也就是0 XXX XXXX
傅物。
??該字節(jié)的第一位有時候也稱為偶校驗位,加上二進制編碼的剩余7位琉预,恰好是一個字節(jié)董饰。ASCII碼規(guī)定8個二進制位的最高一位是0,剩下的7位可以給出128個編碼圆米,表示128個不同的字符卒暂。其中95個編碼,對應(yīng)著計算機終端能敲入并且可以顯示的95個字符娄帖,比如大小寫的26個英文字母也祠,數(shù)字和標(biāo)點符號等;另外的33個字符近速,編碼值為0~31和127诈嘿,他們被用作控制碼绒尊,控制計算機設(shè)備和軟件的一些運行情況蛙奖。
一開始,計算機只在美國使用见秤,所以大家都使用ASCII碼來處理英文沒什么問題佩耳,后來遂蛀,隨著計算機的普及谭跨,越來越多的國家都開始使用計算機干厚,而每個國家的語言并不是相同的,這就產(chǎn)生了一個問題螃宙,對一些國家來說蛮瞄,常規(guī)的ASCII碼所表示的128個字符是不夠用的,所以就引申出了ISO-8859-1谆扎,GB2312等編碼挂捅。
2. ISO-8859-1
其中,上述ASCII碼滿足不了的國家就包括歐洲的一些國家堂湖,于是這些歐洲國家就決定:
- 將自己使用的語言給編碼到原先一個字節(jié)中閑置的第一位上闲先,范圍也就是從
1000 0000
到1111 1111
,這樣的話无蜂,這一套編碼系統(tǒng)就可以表示256個符號了伺糠,其中0到127和ASCII碼表示的符號是相同的,而128到255則分別表示自己國家所使用的符號斥季。
- 這其中训桶,就包括了西歐國家的
ISO-8859-1
累驮,中歐的ISO-8859-2
,南歐的ISO-8859-3
舵揭,北歐的ISO-8859-4
以及相應(yīng)的阿拉伯谤专,希臘等相關(guān)國家的ISO-8859-X
系列,其中午绳,ISO-8859-1
又名Latin-1
置侍,而ISO-8859-2
又名Latin-2
等,而有關(guān)相應(yīng)的ISO-8859系列的詳細說明可參考維基百科:維基百科-ISO/IEC 8859 詳細介紹
3. GB2312
??我們把視線轉(zhuǎn)回國內(nèi)箱叁,在20世紀后期計算機在國內(nèi)開始普及的時候墅垮,同樣也遇到了相同的問題,并且由于中華文化博大精深耕漱,漢字的發(fā)展源遠流長算色,光常用的漢字就有幾千多個,很顯然即使把ASCII碼的第一位也用上螟够,也保存不了多少個漢字灾梦。所以呢,國內(nèi)的開發(fā)者就整出了一套新的編碼方式妓笙,不過我們先來看下區(qū)位碼若河。
3.1 區(qū)位碼
??GB2312編碼中對漢字進行了“分區(qū)”處理,編號為01區(qū)至94區(qū)寞宫;每區(qū)含有94個字符萧福,編號為01位至94位。這種表示方式也稱為區(qū)位碼辈赋,每一個字符都由與其唯一對應(yīng)的區(qū)號和位號所確定鲫忍,區(qū)位碼一般用十進制來表示,其中:
- 01–09區(qū)為符號钥屈,數(shù)字悟民;
- 16–55區(qū)為一級漢字,約3755個篷就,按漢語拼音字母/筆形順序排列射亏;
- 56–87區(qū)為二級漢字,約3008個竭业,按部首/筆畫排序排列智润;
- 10-15區(qū)和88-94區(qū) 未使用空白區(qū);
舉例來說未辆,“啊”字是第16區(qū)中的第1個字符窟绷,它的區(qū)位碼就是1601;“琛”字是第72區(qū)的第41個字符鼎姐,它的區(qū)位碼就是7241钾麸。然后我們再來看一下具體的編碼方式更振。
3.2 GB2312編碼
- 首先,一個字節(jié)1到127內(nèi)的符號不變饭尝,和ASCII碼保持一致肯腕;然后再使用一個字節(jié)和前面的那個字節(jié)進行連接,通過兩個字節(jié)來表示一個漢字钥平,其中第一個字節(jié)稱為高位字節(jié)实撒,第二個字節(jié)稱為低位字節(jié);
- 高位字節(jié)= 區(qū)號 + 0xA0
- 低位字節(jié)= 位號 + 0xA0
舉例:“啊”的區(qū)位碼是1601涉瘾,
高位字節(jié) = 0x10(=16) + 0xA0 = 0xB0
低位字節(jié) = 0x01(=1) + 0xA0 = 0xA1
所以知态,“啊”的編碼就是0xB0(第一個編碼單元)0xA1(第二個編碼單元),所以最終編碼就是B0A1立叛。
不過需要簡單說明的是:
- 高位字節(jié)編碼從0xA1到 0xF7(把01–87區(qū)的區(qū)號加上0xA0)负敏,低位字節(jié)編碼從0xA1到0xFE(把01–94加上0xA0),這樣我們就可以組合出大約7000多個簡體漢字了秘蛇。
- 在這些編碼里其做,我們還把數(shù)學(xué)符號、羅馬希臘的字母赁还、日文的假名們都編進去了妖泄,連在 ASCII 里本來就有的數(shù)字、標(biāo)點艘策、字母都統(tǒng)統(tǒng)重新編了兩個字節(jié)長的編碼蹈胡,這就是常說的”全角”字符,而原來在127號以下的那些就叫”半角”字符了朋蔫。
- 編碼完成之后罚渐,不管是高位字節(jié)還是低位字節(jié),它們的最高位都是1斑举,這樣就可以和ASCII字符的編碼區(qū)分開搅轿,所以說病涨,GB2312是對ASCII碼的中文擴展富玷。
另外,區(qū)位碼這個東西可以理解為一種字符集既穆,而GB2312則可以算作支持該字符集的編碼赎懦,這樣的話其實有點類似于我們后面要說的Unicode和UTF-8的關(guān)系。
4. GBK幻工,Big5励两,GB18030
??GB2312雖然收錄了常用的簡體中文,但由于漢字實在太多囊颅,還包括一些不常用的生僻字及繁體中文当悔,所以GBK就誕生了傅瞻。GBK是在GB2312的基礎(chǔ)上,將生僻字盲憎,繁體中文嗅骄,還有日韓漢字,以及GB2312中不包含的漢字部首符號饼疙、豎排標(biāo)點符號等都收錄進去溺森,組成了GBK編碼。
- GBK總體編碼范圍是0x8140到0xFEFE窑眯,高位字節(jié)范圍是0x81-0xFE屏积,低位字節(jié)范圍是0x40-7E和0x80-0xFE,其中低位字節(jié)不包括0x7F的組合磅甩。
Big5:至于Big5炊林,算是和GB2312同一時期的,是臺灣卷要,香港等地流行的主要基于繁體中文的雙字節(jié)字符集铛铁,同樣使用了兩個字節(jié),第一個字節(jié)稱為高位字節(jié)却妨,第二個字節(jié)稱為低位字節(jié)饵逐,高位字節(jié)的范圍在0x81-0xFE,低位字節(jié)的范圍在0x40-0x7E以及0xA1-0xFE彪标。要了解更多Big5的倍权,可參考:維基百科-大五碼(Big5)
GB18030:而GB18030,則是在GBK的基礎(chǔ)上捞烟,新收錄了少數(shù)民族的字符薄声,及GBK不支持的字符。GBK和GB2312都是雙字節(jié)編碼题画,而GB18030則是變長字節(jié)編碼默辨,變長字節(jié)編碼,包含了單字節(jié)苍息、雙字節(jié)和四字節(jié)三種方式:
GB18030的單字節(jié)編碼范圍是0x00-0x7F缩幸,與ASCII碼完全一致;而雙字節(jié)編碼的范圍和GBK相同竞思,高字節(jié)是0x81-0xFE表谊,低字節(jié)的編碼范圍是0x40-0x7E和0x80-0xFE;四字節(jié)編碼中第一盖喷、三字節(jié)的編碼范圍是0x81-0xFE爆办,第二、四字節(jié)的范圍是0x30-0x39课梳。
5. Unicode
??由于當(dāng)時各個國家都像中國這樣搞出一套自己的編碼標(biāo)準(zhǔn)距辆,結(jié)果互相之間誰也不懂誰的編碼余佃,誰也不支持別人的編碼。當(dāng)時的中國人想讓電腦顯示漢字跨算,就必須裝上一個”漢字系統(tǒng)”咙冗,專門用來處理漢字的顯示、輸入的問題漂彤,裝錯了字符系統(tǒng)雾消,顯示就會亂了套。這怎么辦挫望?就在這時立润,一個叫 ISO (國際標(biāo)誰化組織)的國際組織決定著手解決這個問題。他們采用的方法很簡單:廢了所有的地區(qū)性編碼方案媳板,重新搞一個包括了地球上所有文化桑腮、所有字母和符號的編碼!他們打算叫它“Universal Multiple-Octet Coded Character Set”蛉幸,簡稱 UCS, 俗稱 UNICODE
破讨。
- Unicode 開始制訂時,計算機的存儲器容量極大地發(fā)展了奕纫,空間再也不成為問題了提陶。于是 ISO 就直接規(guī)定必須用兩個字節(jié),也就是16位來統(tǒng)一表示所有的字符匹层,對于 ASCII 里的那些”半角”字符隙笆,UNICODE 包持其原編碼不變,只是將其長度由原來的8位擴展為16位升筏,而其他文化和語言的字符則全部重新統(tǒng)一編碼撑柔。由于”半角”英文符號只需要用到低8位,所以其高 8位永遠是0您访,因此這種大氣的方案在保存英文文本時會多浪費一倍的空間铅忿。
- 在表示一個Unicode的字符時,通常會用“U+”然后緊接著一組十六進制的數(shù)字來表示這一個字符灵汪。Unicode的編碼范圍從U+0000到U+10FFFF檀训,共有1,112,064個碼位(code point)可用來映射字符,Unicode的編碼空間可以劃分為17個平面(plane)识虚,每個平面包含216(65,536)個碼位肢扯。
- 17個平面的碼位可表示為從U+xx0000到U+xxFFFF妒茬,其中xx表示十六進制值從00到10担锤,共計17個平面。第一個平面稱為基本多語言平面(Basic Multilingual Plane, BMP)乍钻,或稱第零平面(Plane 0)肛循。其他平面稱為輔助平面(Supplementary Planes)铭腕。基本多語言平面內(nèi)多糠,從U+D800到U+DFFF之間的碼位區(qū)塊是永久保留不映射到Unicode字符累舷。
Unicode的編碼方式是UCS-2,也就是雙字節(jié)的編碼方式夹孔,針對的是基本多語言平面(BMP)被盈,而相應(yīng)的輔助平面,則是對應(yīng)UCS-4搭伤。
6. UTF系列
- Unicode雖然能覆蓋世界上所有的符號只怎,但由于Unicode 在制訂時沒有考慮與任何一種現(xiàn)有的編碼方案保持兼容,這使得Unicode和另一種編碼進行轉(zhuǎn)換時怜俐,沒有什么直接的方式身堡,必須通過查表來進行。所以Unicode 在很長一段時間內(nèi)都沒有得到很大的推廣普及拍鲤。并且由于計算機網(wǎng)絡(luò)的興起贴谎,Unicode如何在網(wǎng)絡(luò)上傳輸也是一個很重要的問題。
這時候季稳,就出現(xiàn)了面向傳輸?shù)腢nicode的實現(xiàn)方式擅这。一個字符的Unicode編碼是確定的。但是在實際傳輸過程中景鼠,由于不同系統(tǒng)平臺的設(shè)計不一定一致蕾哟,以及出于節(jié)省空間的目的,對Unicode編碼的實現(xiàn)方式有所不同莲蜘。Unicode的實現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format谭确,簡稱為UTF),其中UTF家族比較出名的就是UTF-8票渠,UTF-16逐哈,UTF-32等。
6.1 UTF-8
??UTF-8问顷,是一種變長的編碼方式昂秃,它可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度杜窄。
比如說如果一個僅包含基本7位 ASCII 字符的Unicode符號肠骆,如果每個字符都使用2字節(jié)的原Unicode編碼傳輸,其第一字節(jié)的8位始終為0塞耕,這就造成了比較大的浪費蚀腿。對于這種情況,就可以使用UTF-8編碼,因為這是一種變長編碼莉钙,它將基本7位ASCII字符仍用7位編碼表示廓脆,占用一個字節(jié)(首位補0)。
UTF-8的編碼方式比較簡單:
- 對于單字節(jié)的符號磁玉,字節(jié)的第一位設(shè)為0停忿,后面7位為這個符號的 Unicode 碼。因此對于英語字母蚊伞,UTF-8 編碼和 ASCII 碼是相同的席赂;
- 對于n字節(jié)的符號(n > 1),第一個字節(jié)的前n位都設(shè)為1时迫,第n + 1位設(shè)為0氧枣,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進制位别垮,全部為這個符號的 Unicode 碼便监;
編碼規(guī)則如下(上面是16進制范圍,下面是二進制范圍):
Unicode符號范圍(16進制) | 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 |
Unicode符號范圍(二進制) | UTF-8編碼方式(二進制) |
---|---|
00000000-01111111 | 0xxxxxxx |
00000000 10000000-00000111 11111111 | 110xxxxx 10xxxxxx |
00001000 00000000-11111111 11111111 | 1110xxxx 10xxxxxx 10xxxxxx |
00000001 00000000 00000000 - 00010000 11111111 11111111 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
根據(jù)上表碳想,解讀 UTF-8 編碼非常簡單烧董。如果一個字節(jié)的第一位是0,則這個字節(jié)單獨就是一個字符胧奔;如果第一位是1逊移,則連續(xù)有多少個1,就表示當(dāng)前字符占用多少個字節(jié)龙填,其中中文一般都是在三個字節(jié)的范圍內(nèi)胳泉。
例如”嚴”字的Unicode編碼是\u4E25。0x4E25在0x0800~0xFFFF之間岩遗,所以使用第三行模板 1110xxxx 10xxxxxx 10xxxxxx扇商。將4E25寫成二進制是:0100 1110 0010 0101,然后宿礁,從嚴的最后一個二進制位開始案铺,依次從后向前填入格式中的x,多出的位補0梆靖,得到 11100100 10111000 10100101控汉,也就是 0xE4B8A5 ,這就是嚴字的UTF8編碼返吻。
參考:維基百科-UTF-8
6.2 UTF-16姑子,UTF-32
??UTF-16也是一種變長的編碼方式,它可以使用兩個字節(jié)或四個字節(jié)來表示一個符號测僵。其實UTF-16與Unicode的表示方式UCS-2是對應(yīng)的街佑,在沒有輔助平面字符(surrogate code points)前,UTF-16與UCS-2所指的是同一的意思。但當(dāng)引入輔助平面字符后舆乔,就稱為UTF-16了岳服。
不過UTF-16在表示BMP字符的時候剂公,有一個問題:
- 在Mac機和個人PC上希俩,對字節(jié)順序的理解是不一致的。這時同一字節(jié)流可能會被解釋為不同內(nèi)容纲辽,如某字符為十六進制編碼4E59颜武,按兩個字節(jié)拆分為4E和59,在Mac上讀取時是從低字節(jié)開始拖吼,那么在Mac OS會認為此4E59編碼為594E鳞上,找到的字符為“奎”,而在Windows上從高字節(jié)開始讀取吊档,則編碼為U+4E59的字符為“乙”篙议。就是說在Windows下以UTF-16編碼保存一個字符“乙”,在Mac OS環(huán)境下打開會顯示成“奎”怠硼;
- 此類情況說明UTF-16的編碼順序若不加以人為定義就可能發(fā)生混淆鬼贱,于是在UTF-16編碼實現(xiàn)方式中使用了大端序(
Big-Endian
,簡寫為UTF-16 BE
)香璃、小端序(Little-Endian
这难,簡寫為UTF-16 LE
)的概念,以及可附加的字節(jié)順序記號(BOM)解決方案葡秒,目前在PC機上的Windows系統(tǒng)和Linux系統(tǒng)對于UTF-16編碼默認使用UTF-16 LE姻乓。
至于Big-Endian,是第一個字節(jié)在前眯牧,而Little-Endian蹋岩,則是第二個字節(jié)在前。
而所謂的字節(jié)順序記號学少,在UTF-16表示為在文件或字符串流的最前面加入一個表示編碼順序的字符星澳,這個字符的名字叫做"零寬度非換行空格"(zero width no-break space),用0xFE 0xFF表示旱易。這正好是兩個字節(jié)禁偎,而且FF比FE大1。
- 如果一個文本文件的頭兩個字節(jié)是FE FF阀坏,就表示該文件采用大端序方式如暖;
- 如果頭兩個字節(jié)是FF FE,就表示該文件采用小端序方式忌堂。
至于UTF-32的編碼盒至,則對應(yīng)于USC-4,也就是4個字節(jié)。與其他UTF相比枷遂,UTF-32的編碼長度是固定的樱衷,UTF-32中的每個32位值代表一個Unicode碼位,并且與該碼位的數(shù)值完全一致酒唉。
UTF-32的主要優(yōu)點是可以直接由Unicode碼位來索引矩桂,相比之下,其他可變長度編碼需要進行"循序訪問"操作才能在編碼序列中找到第N個編碼痪伦;而UTF-32的主要缺點是每個碼位使用四個字節(jié)侄榴,空間浪費較多。
這里參考自:
維基百科-字節(jié)順序標(biāo)記
維基百科-UTF-16介紹
維基百科-Unicode介紹
7. emoji表情問題
??該問題是說Emoji表情在保存到Mysql的時候保存失敗网沾,提示亂碼癞蚕。
??這里要簡單說下Emoji表情了,Emoji表情是一種特殊的字符辉哥,而不是像QQ表情一樣的普通字符的轉(zhuǎn)義表示桦山。在Unicode編碼中,占用了U+1F300到U+1F64F中的部分范圍:
- Emoji字符的特殊之處在于醋旦,其使用的Unicode字符超出了通常使用的三字節(jié)UTF-8編碼的Unicode范圍恒水,即BMP范圍U+0000到U+FFFF。按照UTF-8編碼規(guī)范浑度,Emoji字符屬于輔助平面范圍寇窑,通常對應(yīng)4字節(jié)的UTF-8編碼;
- 而Mysql的UTF-8最多只支持3個字節(jié)來表示一個符號箩张,表示的只是17個平面中的基本多語言面 (Basic Multilingual Plane,BMP)字符甩骏,所以保存不了Emoji表情;
所以先慷,這里就引申出了Mysql的utf8mb4字符集(其實就是4字節(jié) UTF-8 Unicode 編碼)饮笛,utf8mb4 字符集使用最多四個字節(jié)擴展UTF-8:
- 對于 BMP字符 UTF8 和 utf8mb4 存儲時是完全一樣的;
- 對于其他平面的字符论熙,utf8mb4通過使用四個字節(jié)來存儲福青,并且utf8mb4是完全兼容UTF-8,因此從舊版本的MySQL UTF-8 升級數(shù)據(jù)時 不用擔(dān)心字符轉(zhuǎn)換或丟失數(shù)據(jù)脓诡;
不過需要注意的是无午,utf8mb4是mysql在5.5版本之后引入的字符集,并不是諸如UTF-8祝谚,UTF-16等通用的字符集宪迟。
8. 總結(jié)
- 可以這么理解,Unicode是字符集交惯,ASCII次泽、GB2312穿仪、GBK、GB18030既是字符集也是編碼方式意荤,UTF-8只是編碼方式啊片;
- Unicode從開始到現(xiàn)在一直在不斷增修,每個新版本都加入更多新的字符玖像。目前最新的版本為2018年6月5日公布的11.0.0紫谷,已經(jīng)收錄超過13萬個字符。Unicode涵蓋的數(shù)據(jù)除了視覺上的字形御铃、編碼方法碴里、標(biāo)準(zhǔn)的字符編碼外沈矿,還包含了字符特性上真,如大小寫字母。
- 一開始羹膳,Unicode的2 字節(jié)形式通常稱作 UCS-2睡互,然而,由于2 字節(jié)數(shù)量的限制陵像,UCS-2 只能表示最多 65536 個字符就珠,所以后來有了4字節(jié),Unicode 的 4 字節(jié)形式被稱為 UCS-4 或 UTF-32醒颖,能夠定義 Unicode 的全部擴展妻怎,最多可定義 100 萬個以上唯一字符。UCS 只是規(guī)定了如何編碼泞歉,并沒有規(guī)定如何傳輸逼侦、保存編碼,所以有些人說 Unicode編碼占用兩個字節(jié)腰耙,這種說法是不準(zhǔn)確的榛丢。
本文參考自:維基百科
潛行者m-網(wǎng)頁編碼就是那點事
阮一峰-字符編碼筆記:ASCII,Unicode 和 UTF-8
UTF-16與UCS-2的區(qū)別
IBM developerWorks-深入分析 Java 中的中文編碼問題
《Java核心技術(shù)I》