万字长文讲解编码知识,看这文就够了!
作者?|?Aledsan?
責(zé)編?|?王曉曼?
出品?|?CSDN博客
基礎(chǔ)概念
1、字符
字符指類(lèi)字形單位或符號(hào),包括字母、數(shù)字、運(yùn)算符號(hào)、標(biāo)點(diǎn)符號(hào)和其他符號(hào),以及一些功能性符號(hào)。一般來(lái)說(shuō)我們稱(chēng)某個(gè)字符集里面的字符,叫xx字符,如ASCII字符集里面的ASCII字符,GB2312字符集里面的GB2312字符。
2、字符集
字符集(Character Set、Charset),字面上的理解就是字符的集合,是一個(gè)自然語(yǔ)言文字系統(tǒng)支持的所有字符的集合。字符是各種文字和符號(hào)的總稱(chēng),包括文字、數(shù)字、字母、音節(jié)、標(biāo)點(diǎn)符號(hào)、圖形符號(hào)等。例如ASCII字符集,定義了128個(gè)字符;GB2312字符集定義了7445個(gè)字符。而字符集準(zhǔn)確地來(lái)說(shuō),指的是已編號(hào)的字符的有序集合(但不一定是連續(xù)的,后文有詳細(xì)介紹)。
常見(jiàn)字符集名稱(chēng):ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。
3、碼位
在字符編碼術(shù)語(yǔ)中,碼位(code point)或稱(chēng)編碼位置、碼點(diǎn),是組成碼空間(或代碼頁(yè))的數(shù)值。例如,ASCII碼包含128個(gè)碼位,范圍是016進(jìn)制到7F16進(jìn)制,擴(kuò)展ASCII碼包含256個(gè)碼位,范圍是016進(jìn)制到FF16進(jìn)制,而Unicode包含1,114,112個(gè)碼位,范圍是016進(jìn)制到10FFFF16進(jìn)制。Unicode碼空間劃分為17個(gè)Unicode字符平面(基本多文種平面,16個(gè)輔助平面),每個(gè)平面有65,536(= 216)個(gè)碼位。因此Unicode碼空間總計(jì)是17 × 65,536 = 1,114,112. —解釋來(lái)源于維基百科。
4、字符編碼
字符編碼(Character Encoding),是把字符集中的字符按一定方式編碼為某指定集合中的某一對(duì)象的過(guò)程(比如將字符編碼為由0和1兩個(gè)數(shù)字所組成的位串模式、由0~9十個(gè)數(shù)字所組成的自然數(shù)序列或電脈沖等),亦即在字符集與指定集合兩者之間建立一個(gè)對(duì)應(yīng)關(guān)系(即映射關(guān)系)的過(guò)程。這是信息處理的一項(xiàng)基礎(chǔ)技術(shù)。常見(jiàn)的例子包括將拉丁字母表編碼成摩斯電碼和ASCII碼。
PS:這里我們計(jì)算機(jī)這里字符編碼肯定是用二進(jìn)制來(lái)編碼的。
看完這四個(gè)概念,你應(yīng)該要明白,它們之間的關(guān)系,以ASCII為例,下圖解釋它們之間關(guān)系。
這里細(xì)說(shuō)一下,碼位就是這個(gè)字符集里面字符的一個(gè)表示位置,通俗來(lái)說(shuō),碼位就是一般跟字符集綁在一起,字符編碼是把字符集中的字符編碼為特定的二進(jìn)制數(shù),以便在計(jì)算機(jī)中存儲(chǔ)。這個(gè)二進(jìn)制數(shù)就叫xx碼。
字符集編碼分類(lèi)總結(jié)
在說(shuō)字符集編碼之前,先明確一個(gè)觀(guān)點(diǎn),字符集編碼與字符集是兩個(gè)不同層面的概念:
(1)charset 是character set 的簡(jiǎn)寫(xiě),即字符集。
(2)encoding 是 charsetencoding 的簡(jiǎn)寫(xiě),即字符集編碼,簡(jiǎn)稱(chēng)編碼。
1、ASCII編碼
ASCII(美國(guó)信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母(就是我們現(xiàn)在的英文字母)的一套電腦編碼系統(tǒng)。它主要用于顯示現(xiàn)代英語(yǔ),而其擴(kuò)展版本延伸美國(guó)標(biāo)準(zhǔn)信息交換碼則可以部分支持其他西歐語(yǔ)言,并等同于國(guó)際標(biāo)準(zhǔn)ISO/IEC 646。
ASCII由電報(bào)碼發(fā)展而來(lái)。第一版標(biāo)準(zhǔn)發(fā)布于1963年,1967年經(jīng)歷了一次主要修訂,最后一次更新則是在1986年,至今為止共定義了128個(gè)字符;其中33個(gè)字符無(wú)法顯示(一些終端提供了擴(kuò)展,使得這些字符可顯示為諸如笑臉、撲克牌花式等8-bit符號(hào)),且這33個(gè)字符多數(shù)都已是陳廢的控制字符。控制字符的用途主要是用來(lái)操控已經(jīng)處理過(guò)的文字。在33個(gè)字符之外的是95個(gè)可顯示的字符。用鍵盤(pán)敲下空白鍵所產(chǎn)生的空白字符也算1個(gè)可顯示字符(顯示為空白)。
每個(gè)ASCII字符占用1個(gè)字節(jié)(8bits),共有128位字符或符號(hào),使用7位二進(jìn)制數(shù)(剩下的1位二進(jìn)制為0,即高位為0)來(lái)表示所有的大寫(xiě)和小寫(xiě)字母,數(shù)字0 到9、標(biāo)點(diǎn)符號(hào),以及在美式英語(yǔ)中使用的特殊控制字符。
缺點(diǎn):ASCII的最大缺點(diǎn)是只能顯示26個(gè)基本拉丁字母、阿拉伯?dāng)?shù)目字和英式標(biāo)點(diǎn)符號(hào),因此只能用于顯示現(xiàn)代美國(guó)英語(yǔ)(而且在處理英語(yǔ)當(dāng)中的外來(lái)詞如na?ve、café、élite等等時(shí),所有重音符號(hào)都不得不去掉,即使這樣做會(huì)違反拼寫(xiě)規(guī)則)。而EASCII(即擴(kuò)展的ASCII碼,利用8位的高位設(shè)為1進(jìn)行擴(kuò)展)雖然解決了部分西歐語(yǔ)言的顯示問(wèn)題,但對(duì)更多其他語(yǔ)言依然無(wú)能為力。因此現(xiàn)在的操作系統(tǒng)內(nèi)碼(稍后會(huì)講)基本已經(jīng)拋棄ASCII碼而轉(zhuǎn)用Unicode碼。
ASCII碼表 :http://www.asciitable.com
2、GB2312編碼
前面可以看到ASCII碼即使進(jìn)行了擴(kuò)展也能表示的字符也很少,尤其是當(dāng)需要計(jì)算機(jī)顯示存儲(chǔ)中文的時(shí)候,就需要一種對(duì)中文進(jìn)行編碼的字符集,GB 2312就是解決中文編碼的字符集,由國(guó)家標(biāo)準(zhǔn)委員會(huì)發(fā)布。那個(gè)時(shí)候當(dāng)中國(guó)人們得到計(jì)算機(jī)時(shí),已經(jīng)沒(méi)有可以利用的字節(jié)狀態(tài)來(lái)表示漢字,況且有6000多個(gè)常用漢字需要保存,于是想到把那些ASCII碼中127號(hào)之后的奇異符號(hào)們直接取消掉, 規(guī)定:一個(gè)小于127的字符的意義與原來(lái)相同,但兩個(gè)大于127的字符連在一起時(shí),就表示一個(gè)漢字,前面的一個(gè)字節(jié)(稱(chēng)之為高字節(jié))從0xA1用到0xF7,后面一個(gè)字節(jié)(低字節(jié))從0xA1到0xFE,這樣我們就可以組合出大約7000多個(gè)簡(jiǎn)體漢字了。在這些編碼里,我們還把數(shù)學(xué)符號(hào)、羅馬希臘的字母、日文的假名們都編進(jìn)去了,連在 ASCII 里本來(lái)就有的數(shù)字、標(biāo)點(diǎn)、字母都統(tǒng)統(tǒng)重新編了兩個(gè)字節(jié)長(zhǎng)的編碼,這就是常說(shuō)的全角字符,而原來(lái)在127號(hào)以下的那些就叫半角字符了。這種漢字方案叫做 “GB2312”。GB2312 是對(duì)ASCII 的中文擴(kuò)展。兼容ASCII。
這里的A GB2312碼是0xA3C1,0xA3和0xC1都是高于127的,所以判斷它是個(gè)全角字符,另外我們可以觀(guān)察到所有的GB2312編碼表中的GB2312碼每個(gè)字節(jié)都是大于0xA0的,這個(gè)就是為了保證能區(qū)分是否為ASCII碼,小于127的字節(jié)就按照ASCII碼標(biāo)準(zhǔn),碰到連續(xù)兩個(gè)大于127字節(jié)就組合成一個(gè)GB2312碼。
GB2312漢字編碼字符集對(duì)照表:http://tools.jb51.net/table/gb2312
3、GBK編碼
但是中國(guó)的漢字太多了,我們很快就就發(fā)現(xiàn)有許多人的人名沒(méi)有辦法在這里打出來(lái),不得不繼續(xù)把 GB2312 沒(méi)有用到的碼位找出來(lái)用上。后來(lái)還是不夠用,于是干脆不再要求低字節(jié)一定是127號(hào)之后的內(nèi)碼,只要第一個(gè)字節(jié)是大于127就固定表示這是一個(gè)漢字的開(kāi)始,不管后面跟的是不是擴(kuò)展字符集里的內(nèi)容。結(jié)果擴(kuò)展之后的編碼方案被稱(chēng)為 “GBK” 標(biāo)準(zhǔn),GBK 包括了GB2312 的所有內(nèi)容,同時(shí)又增加了近20000個(gè)新的漢字(包括繁體字)和符號(hào)。
4、GB18030編碼
后來(lái)中國(guó)的少數(shù)民族也要用電腦了,GBK的兩萬(wàn)多字也已經(jīng)無(wú)法滿(mǎn)足我們的需求了,還有更多可能你自己從來(lái)沒(méi)見(jiàn)過(guò)的漢字需要編碼。這時(shí)候顯然只用2bytes表示一個(gè)字已經(jīng)不夠用了(2byte最多只有65536種組合,然而為了和ASCII兼容,最高位不能為0就已經(jīng)直接淘汰了一半的組合,只剩下3萬(wàn)多種組合無(wú)法滿(mǎn)足全部漢字要求)。因此GB18030多出來(lái)的漢字使用4byte編碼。
當(dāng)然,為了兼容GBK,這個(gè)四字節(jié)的前兩位顯然不能與GBK沖突(實(shí)操中發(fā)現(xiàn)后兩位也并沒(méi)有和GBK沖突)。通過(guò)多年的發(fā)展至此,GB18030編碼的中文文件已經(jīng)有七萬(wàn)多個(gè)漢字了。
GB18030包含三種長(zhǎng)度的編碼:單字節(jié)的ASCII、雙字節(jié)的GBK(略帶擴(kuò)展)、以及用于填補(bǔ)所有Unicode碼位的四字節(jié)UTF區(qū)塊。所以我們說(shuō)GB18030采用多字節(jié)編碼,每個(gè)字符可以由 1 個(gè)、2 個(gè)或 4 個(gè)字節(jié)組成。
其實(shí)我們用到的99%以上的漢字,都在GB2312那一塊區(qū)域內(nèi)。在實(shí)際使用中,GBK編碼已經(jīng)可以滿(mǎn)足大部分場(chǎng)景了,GB18030編碼中所有漢字都是我們這輩子都不一定能見(jiàn)到的文字,所以平時(shí)經(jīng)常會(huì)使用的就是GBK編碼。
這里額外總結(jié)一下這四個(gè)兼容性關(guān)系是GB18030兼容GBK,GBK兼容GB2312,GB2312兼容ASCII。所謂兼容,你可以簡(jiǎn)單理解為子集、不沖突的關(guān)系。
例如GB2312編碼的文件中可以出現(xiàn)ASCII字符,GBK編碼的文件中可以出現(xiàn)GB2312和ASCII字符,GB18030編碼的文件可以出現(xiàn)GBK、GB2312、ASCII字符。
5、Unicode
友情建議:看Unicode一些概念解釋和歷史時(shí),建議看維基百科,而且是英文版的,別用中文版,有些地方講的模棱兩可,容易陷入盲區(qū)。
Unicode(中文:萬(wàn)國(guó)碼、國(guó)際碼、統(tǒng)一碼、單一碼)(全稱(chēng)Universal Multiple-Octet Coded Character Set)它伴隨著通用字符集(英語(yǔ):Universal Character Set, UCS)的標(biāo)準(zhǔn)而發(fā)展。所以可以看出他是字符集。
(1)Unicode與 ISO 10646
全世界很多個(gè)國(guó)家都在為自己的文字編碼,并且互不相通,不同的語(yǔ)言字符編碼值相同卻代表不同的符號(hào)(例如:韓文編碼EUC-KR中“???”的編碼值正好是漢字編碼GBK中的“茄憊絹”)。
因此,同一份文檔,拷貝至不同語(yǔ)言的機(jī)器,就可能成了亂碼,于是人們就想:我們能不能定義一個(gè)超大的字符集,它可以容納全世界所有的文字字符,再對(duì)它們統(tǒng)一進(jìn)行編碼,讓每一個(gè)字符都對(duì)應(yīng)一個(gè)不同的編碼值,從而就不會(huì)再有亂碼了。
如果說(shuō)“各個(gè)國(guó)家都在為自己文字獨(dú)立編碼”是百家爭(zhēng)鳴,那么“建立世界統(tǒng)一的字符編碼”則是一統(tǒng)江湖,誰(shuí)都想來(lái)做這個(gè)武林盟主。早前就有兩個(gè)機(jī)構(gòu)做了這個(gè)事:
國(guó)際標(biāo)準(zhǔn)化組織(ISO),他們于1984年創(chuàng)建ISO/IEC JTC1/SC2/WG2工作組,試圖制定一份“通用字符集”(Universal Character Set,簡(jiǎn)稱(chēng)UCS),并最終制定了ISO 10646標(biāo)準(zhǔn)。(簡(jiǎn)單來(lái)說(shuō)ISO 10646標(biāo)準(zhǔn)就是UCS)
統(tǒng)一碼聯(lián)盟,他們由Xerox、Apple等軟件制造商于1988年組成,并且開(kāi)發(fā)了Unicode標(biāo)準(zhǔn)(The Unicode Standard,這個(gè)前綴Uni很牛逼哦—Unique, Universal, and Uniform)。
Unicode與ISO 10646標(biāo)準(zhǔn)的風(fēng)風(fēng)雨雨:?
在1984年,喜歡以繁多的編號(hào)糊弄群眾的國(guó)際標(biāo)準(zhǔn)化組織ISO也開(kāi)始著手制定解決不同語(yǔ)言字符數(shù)量太大問(wèn)題的解決方案,這一方案被稱(chēng)為Universal Character Set(UCS),正式的編號(hào)是ISO-10646(記得么,ASCII是ISO-646,不知這種安排是否是故意的)。
還是ISO高瞻遠(yuǎn)矚,一開(kāi)始就確定了UCS是一個(gè)31位的編碼字符集(即用一個(gè)大小不超過(guò)2的31次方的整數(shù)數(shù)字為每個(gè)字符編號(hào)),這回真的足以容納古往今來(lái)所有國(guó)家,所有語(yǔ)言所包含的字符了(是的,任何國(guó)家,任何小語(yǔ)種都包括)。雖然后來(lái)他們意識(shí)到,2的31次方個(gè)碼位又實(shí)在太多了……
天下大勢(shì),分久必合。無(wú)論Unicode還是UCS,最初的目的都是杜絕各種各樣名目繁多形式各異互不兼容老死不相往來(lái)的私用擴(kuò)展編碼(好啰嗦的一句話(huà)),結(jié)果兩方確立標(biāo)準(zhǔn)的同時(shí)(最初時(shí)這兩個(gè)標(biāo)準(zhǔn)是不兼容的),因?yàn)槎际莻€(gè)干個(gè)的,肯定不可能一模一樣,出現(xiàn)標(biāo)準(zhǔn)不同。
1991年,Unicode聯(lián)盟與ISO的工作組終于開(kāi)始討論Unicode與UCS的合并問(wèn)題,雖然其后的合并進(jìn)行了很多年,Unicode初版規(guī)范中的很多編碼都需要被改寫(xiě),UCS也需要對(duì)碼空間的使用進(jìn)行必要限制,但成果是喜人的。
最終,兩者統(tǒng)一了抽象字符集(即任何一個(gè)在Unicode中存在的字符,在UCS中也存在),且最靠前的65535個(gè)字符也統(tǒng)一了字符的編碼。對(duì)于碼空間,兩者同意以一百一十萬(wàn)為限(即兩者都認(rèn)為雖然65536不夠,但2的31次方又太大,一百一十萬(wàn)是個(gè)雙方都可接受的碼空間大小,也夠用,當(dāng)然,這里說(shuō)的一百一十萬(wàn)只是個(gè)約數(shù)),Unicode將碼空間擴(kuò)展到了一百一十萬(wàn),而UCS將永久性的不使用一百一十萬(wàn)以后的碼位。
也就是說(shuō),現(xiàn)在再講Unicode只包含65536個(gè)字符是不對(duì)的(雖然大家現(xiàn)在都知道Unicode至少都可以囊括幾億個(gè)字符)。除了對(duì)已經(jīng)定義的字符進(jìn)行統(tǒng)一外,Unicode聯(lián)盟與ISO工作組也同意今后任何的擴(kuò)展工作兩者均保持同步,因此雖然從歷史的意義上講Unicode與UCS不是一回事(甚至細(xì)節(jié)上說(shuō)也不是一回事),但現(xiàn)在提起Unicode,指代兩者均無(wú)不妥,畢竟因?yàn)橐呀?jīng)統(tǒng)一了。
(現(xiàn)在網(wǎng)上基本上把Unicode字符集叫做UCS,Unicoide的全稱(chēng)是 Universal Multiple-Octet Coded Character Set簡(jiǎn)寫(xiě)也是UCS,一看也對(duì)上了,害,只能說(shuō)天注定)。
現(xiàn)在Unicode編碼點(diǎn)分為17個(gè)平面(plane),每個(gè)平面包含216(即65536)個(gè)碼位(codepoint)。17個(gè)平面的碼位可表示為從U+xx0000到U+xxFFFF,其中xx表示十六進(jìn)制值從0016到1016,共計(jì)17個(gè)平面。
這第一個(gè)位置(當(dāng)xx是00的時(shí)候)被稱(chēng)為BMP(基本多文種平面,BasicMultilingualPlane)。它包含了最常用的碼位從U+0000到U+FFFF(常見(jiàn)的65536個(gè)字符)。
其余16個(gè)平面(從下面的1號(hào)平面到16號(hào)平面),你可以叫做非BMP,由此這樣分的話(huà)里面的字符就有兩個(gè)概念:BMP字符和非BMP字符,后者也被稱(chēng)為補(bǔ)充字符。(偷偷告訴你們,大家喜聞樂(lè)見(jiàn)的Emoji表情都是在1號(hào)平面,范圍是U+1F600-U+1F64F)
(2)UCS-2和UCS-4
ISO10646標(biāo)準(zhǔn)為“通用字符集”(UCS)定義了一種16位的編碼形式(即UCS-2),UCS-2全稱(chēng)Universal Character Set coded in 2octets,從英文上就可以看出含義,以2字節(jié)編碼的通用字符集編碼,固定占用2個(gè)字節(jié),它包含65536個(gè)編碼空間(可以為全世界最常用的63K字符編碼,為了兼容Unicode,0xD800-0xDFFF之間的碼位未使用)。
例:“漢”的UCS-2編碼為6C49。除此之外ISO10646標(biāo)準(zhǔn)為“通用字符集”(UCS)還定義了一種31位的編碼形式(即UCS-4),UCS-4全稱(chēng)UniversalCharacter Set coded in 4 octets,其編碼固定占用4個(gè)字節(jié),編碼空間為0x00000000 ~0x7FFFFFFF(可以編碼20多億個(gè)字符)。
隨著Unicode與ISO 10646合并統(tǒng)一,Unicode就用UCS通用字符集標(biāo)準(zhǔn),早期的 Unicode 編碼實(shí)現(xiàn)也就采用了UCS-2和UCS-4。(準(zhǔn)確來(lái)說(shuō)是UCS-2,UCS-4基本上是理論,沒(méi)付諸實(shí)際,畢竟早期65536個(gè)字符已經(jīng)夠用了,我兩個(gè)字節(jié)編碼能實(shí)現(xiàn)的事,腦子笨的人才會(huì)用四個(gè)字節(jié)實(shí)現(xiàn),你以為存儲(chǔ)和帶寬有多的)。
這里編碼最多也就存在UCS-2(big Endian和LittleEndian先不管,后面會(huì)講)。
Unicode字符集只規(guī)定了碼點(diǎn)和文字之間的對(duì)應(yīng)關(guān)系,并沒(méi)有規(guī)定碼點(diǎn)在計(jì)算機(jī)中如何存儲(chǔ)。UCS-2和UCS-4就規(guī)定了具體的實(shí)現(xiàn),后來(lái)改進(jìn)演變?yōu)榱薝TF-16, UTF-32。然后又創(chuàng)造了一種全新的簡(jiǎn)單粗暴好用的變長(zhǎng)編碼UTF-8,于是乎這三哥們就形成了現(xiàn)代Unicode字符集編碼實(shí)現(xiàn)的三劍客。
(3)UTF-16與UTF-32
Unicode與ISO 10646合并統(tǒng)一后,Unicode與 ISO 10646 的通用字符集概念(UCS)相對(duì)應(yīng)。早期實(shí)現(xiàn)Unicode用的編碼是UCS-2,后來(lái)隨著發(fā)展發(fā)現(xiàn) 216(即65536)個(gè)字符不能滿(mǎn)足了,Unicode標(biāo)準(zhǔn)本身發(fā)生了變化:65536個(gè)字符顯得不足,引入了更大的31位空間和一個(gè)編碼(UCS-4),每個(gè)字符需要4個(gè)字節(jié)。
但是統(tǒng)一碼聯(lián)盟對(duì)此予以抵制(這就是為什么我之前說(shuō)UCS-4是一種理論編碼,根本就沒(méi)付諸實(shí)際),這是因?yàn)槊總€(gè)字符4個(gè)字節(jié)浪費(fèi)了很多磁盤(pán)空間和內(nèi)存,并且因?yàn)橐恍┲圃焐桃呀?jīng)在每個(gè)字符2個(gè)字節(jié)的技術(shù)上投入了大量資金。所以最后通過(guò)一系列巴拉巴拉討論規(guī)定形成了一種折衷方案,建立了UTF-16編碼方案(此時(shí)Unicode標(biāo)準(zhǔn)2.0),它替代了原有的UCS-2,并做了改進(jìn)。
它與UCS-2一樣,它使用兩個(gè)字節(jié)為全世界最常用的63K字符編碼,不同的是,它使用4個(gè)字節(jié)對(duì)不常用的字符進(jìn)行編碼。目的就是為了支持從17個(gè)平面編碼1,112,064個(gè)代碼點(diǎn)。
UTF-16屬于變長(zhǎng)編碼。我們可以將UTF-16編碼看成是UCS-2編碼父集。在沒(méi)有輔助平面字符(surrogate code points)前,UTF-16與UCS-2所指的是同一意思。但當(dāng)引入輔助平面字符后,就稱(chēng)為UTF-16了。
現(xiàn)在應(yīng)該認(rèn)為UCS-2已作廢,如果有人還用這種,也不必糾結(jié),它就是表達(dá)用定長(zhǎng)2字節(jié)編碼,自己心里清楚就行(基本上你查維基百科上UCS-2都是重定向到UTF-16)。
另外當(dāng)時(shí)ISO 10646的UCS-4編碼并入了Unicode標(biāo)準(zhǔn),而UCS-4有20多億個(gè)編碼空間,但實(shí)際使用范圍并不超過(guò)0x10FFFF,并且為了兼容Unicode標(biāo)準(zhǔn),ISO也承諾將不會(huì)為超出0x10FFFF的UCS-4編碼賦值。
由此提出了實(shí)實(shí)在在的UTF-32編碼(現(xiàn)在也應(yīng)該認(rèn)為UCS-4像UCS-2一樣作廢,維基百科上UCS-4也重定向到UTF-32頁(yè)面),它的編碼值與UCS-4相同,只不過(guò)其編碼空間被限定在了0~0x10FFFF之間。因此也可以說(shuō):UTF-32是UCS-4的一個(gè)子集。
(現(xiàn)在若有軟件聲稱(chēng)自己支持UCS-2,那其實(shí)是暗指它不能支持在UTF-16中超過(guò)2字節(jié)的字集。)
UTF-16(16 位 Unicode轉(zhuǎn)換格式)是一種字符編碼,能夠?qū)nicode的所有1,112,064個(gè)有效碼點(diǎn)進(jìn)行編碼(實(shí)際上,此代碼點(diǎn)數(shù)由UTF-16的設(shè)計(jì)決定,這個(gè)你細(xì)品你就知道什么意思,就好像某個(gè)班有55個(gè)人,根據(jù)55個(gè)座位確定55個(gè)人,而55個(gè)座位這個(gè)多少是由55個(gè)人決定的,兩者是相互的,這是一個(gè)哲學(xué)道理,hh扯遠(yuǎn)了,所以其中意味自行明白)。
前面提到過(guò):Unicode編碼點(diǎn)分為17個(gè)平面(plane),每個(gè)平面包含216(即65536)個(gè)碼位(codepoint),而第一個(gè)平面稱(chēng)為“基本多語(yǔ)言平面”(Basic Multilingual Plane,簡(jiǎn)稱(chēng)BMP),其余平面稱(chēng)為“輔助平面”(Supplementary Planes)。其中“基本多語(yǔ)言平面”(00xFFFF)中0xD8000xDFFF之間的碼位作為保留,未使用。
UCS-2只能編碼“基本多語(yǔ)言平面”中的字符,此時(shí)UTF-16與UCS-2的編碼一樣(都直接使用Unicode的碼位作為編碼值),例:“漢”在Unicode中的碼位為6C49,而在UTF-16編碼也為6C49。
另外,UTF-16還可以利用保留下來(lái)的0xD800-0xDFFF區(qū)段的碼位來(lái)對(duì)“輔助平面”的字符的碼位進(jìn)行編碼,因此UTF-16可以為Unicode中所有的字符編碼。
UTF-16和UTF-32也就是如今Unicode編碼的標(biāo)準(zhǔn)之二,他們的區(qū)別就是UTF-16是變長(zhǎng)編碼,大部分是2字節(jié)和少部分4字節(jié),UTF-32是定長(zhǎng)編碼,表示任何字符都用 4 字節(jié)
(4)UTF-8
從前述內(nèi)容可以看出:無(wú)論是UCS-2/4還是UTF-16/32,一個(gè)字符都需要多個(gè)字節(jié)來(lái)編碼,這對(duì)那些英語(yǔ)國(guó)家來(lái)說(shuō)多浪費(fèi)帶寬啊!(尤其在網(wǎng)速本來(lái)就不快的那個(gè)年代......),而且我們注意到UTF-16最少2字節(jié)和UTF-32不變4字節(jié),這肯定是不兼容ASCII碼的,由此,UTF-8產(chǎn)生了。
在UTF-8編碼中,ASCII碼中的字符還是ASCII碼的值,只需要一個(gè)字節(jié)表示,其余的字符需要2字節(jié)、3字節(jié)或4字節(jié)來(lái)表示。
UTF-8的編碼規(guī)則:
對(duì)于ASCII碼中的符號(hào),使用單字節(jié)編碼,其編碼值與ASCII值相同。其中ASCII值的范圍為0~0x7F,所有編碼的二進(jìn)制值中第一位為0(這個(gè)正好可以用來(lái)區(qū)分單字節(jié)編碼和多字節(jié)編碼)。
其它字符用多個(gè)字節(jié)來(lái)編碼(假設(shè)用N個(gè)字節(jié)),多字節(jié)編碼需滿(mǎn)足:第一個(gè)字節(jié)的前N位都為1,第N+1位為0,后面N-1 個(gè)字節(jié)的前兩位都為10,這N個(gè)字節(jié)中其余位全部用來(lái)存儲(chǔ)Unicode中的碼位值。
現(xiàn)如今UTF-8 是互聯(lián)網(wǎng)上使用最廣的一種 Unicode 的實(shí)現(xiàn)方式,是其他兩種無(wú)可比擬的。
(5)UTF的字節(jié)序和BOM
字節(jié)序就要先補(bǔ)充一點(diǎn)知識(shí):
碼元(code unit):是能用于處理或交換編碼文本的最小比特組合。它代表某種編碼中最小的可用來(lái)識(shí)別一個(gè)合法字符的最小字節(jié)數(shù)序列。
UTF-8使用變長(zhǎng)的字節(jié)序列來(lái)表示字符;某個(gè)字符(對(duì)應(yīng)一個(gè)碼點(diǎn))可能使用1-4個(gè)字節(jié)才能表示;在UTF-8中一個(gè)字符最小可能一個(gè)字節(jié),所以我們規(guī)定1個(gè)字節(jié)就是一個(gè)碼元;
UTF-16使用也變長(zhǎng)字節(jié)序列來(lái)表示字符;某個(gè)字符(對(duì)應(yīng)一個(gè)碼點(diǎn))可能使用2個(gè)或者4個(gè)字符來(lái)表示;因?yàn)?個(gè)字節(jié)序列是最小的能夠識(shí)別一個(gè)碼點(diǎn)的單位,同理我們規(guī)定2個(gè)字節(jié)就是一個(gè)碼元;
UTF-32使用定長(zhǎng)的4個(gè)字節(jié)表示一個(gè)字符;一個(gè)字符(對(duì)應(yīng)一個(gè)碼點(diǎn))使用4個(gè)字符來(lái)表示,這樣4個(gè)字節(jié)就是一個(gè)碼元。
簡(jiǎn)單來(lái)說(shuō),就是“碼點(diǎn)”經(jīng)過(guò)映射后得到的二進(jìn)制串的轉(zhuǎn)換格式單位稱(chēng)之為“碼元”。“碼點(diǎn)”就是一串二進(jìn)制數(shù),“碼元”就是切分這個(gè)二進(jìn)制數(shù)的方法。這些編碼每次處理一個(gè)碼元,你可以把它理解為UTF-8每次讀碼點(diǎn)的8位,UTF-16每次讀碼點(diǎn)的16位,UTF-32每次讀碼點(diǎn)的32位,。當(dāng)然這也是為什么叫這些叫Unicode轉(zhuǎn)換格式的原因。處理的是同一個(gè)字符集,但是處理方式不同。
字節(jié)序
UTF-8一次一個(gè)UTF-8碼元,即處理一個(gè)字節(jié),沒(méi)有字節(jié)序的問(wèn)題。UTF-16一次處理一個(gè)UTF-16碼元,對(duì)應(yīng)兩個(gè)字節(jié),UTF-32一次一個(gè)UTF-32碼元,對(duì)應(yīng)處理四個(gè)字節(jié),所以這就要考慮到一個(gè)字節(jié)序問(wèn)題。
以UTF-16w為例,在解釋一個(gè)UTF-16編碼文本前,首先要弄清楚每個(gè)編碼單元的字節(jié)序。例如收到一個(gè)“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。
如果我們收到UTF-16字節(jié)流“594E”,那么這是“奎”還是“乙”?這就考慮大小端問(wèn)題,所以UTF-16編碼包括三種:UTF-16BE(Big Endian),UTF-16LE(Little Endian)、UTF-16(類(lèi)似的名稱(chēng)UCS-2BE和UCS-2LE用于顯示UCS-2的版本。)
UTF-16BE和UTF-16LE好理解,直接指定了字節(jié)序(大小端),但是UTF-16怎么處理呢?
Unicode規(guī)范中推薦的標(biāo)記字節(jié)順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個(gè)有點(diǎn)小聰明的想法:
在UCS編碼中有一個(gè)叫做ZERO WIDTH NO-BREAKSPACE的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應(yīng)該出現(xiàn)在實(shí)際傳輸中。UCS規(guī)范建議我們?cè)趥鬏斪止?jié)流前,先傳輸字符ZERO WIDTH NO-BREAK SPACE。這樣如果接收者收到FEFF,就表明這個(gè)字節(jié)流是Big-Endian的;如果收到FFFE,就表明這個(gè)字節(jié)流是Little-Endian的。
同樣的類(lèi)比,UTF-32也是這樣的。有UTF-32BE、UTF-32LE、UTF-32。前面UTF-32BE和UTF-32LE直接指定了字節(jié)序(大小端),后面的UTF-32也是靠BOM。
UTF-8不需要BOM來(lái)表明字節(jié)順序,但可以用BOM來(lái)表明編碼方式。字符ZERO WIDTH NO-BREAKSPACE的UTF-8編碼是EF BB BF(讀者可以用我們前面介紹的編碼方法驗(yàn)證一下)。所以如果接收者收到以EF BB BF開(kāi)頭的字節(jié)流,就知道這是UTF-8編碼了。
Windows就是使用BOM來(lái)標(biāo)記文本文件的編碼方式的。它就建議所有的 Unicode 文件應(yīng)該以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符開(kāi)頭。這作為一個(gè)“特征符”或“字節(jié)順序標(biāo)記(byte-ordermark,BOM)”來(lái)識(shí)別文件中使用的編碼和字節(jié)順序。所以用Windows自帶的記事本將文件保存為UTF-8編碼的時(shí)候,記事本會(huì)自動(dòng)在文件開(kāi)頭插入BOM(雖然BOM對(duì)UTF-8來(lái)說(shuō)并不是必須的)。
但也有一些系統(tǒng)或程序不支持BOM,因此帶有BOM的Unicode文件有時(shí)會(huì)帶來(lái)一些問(wèn)題。比如JDK1.5以及之前的Reader都不能處理帶有BOM的UTF-8編碼的文件,解析這種格式的xml文件時(shí),會(huì)拋出異常:Content is not allowed inprolog。
Linux/UNIX 并沒(méi)有使用 BOM,因?yàn)樗鼤?huì)破壞現(xiàn)有的 ASCII 文件的語(yǔ)法約定。所以一般我們不建議用Windows自帶的記事本編輯UTF-8文件就是這樣。
總結(jié)
1、簡(jiǎn)單地說(shuō):Unicode和UCS是字符集,不屬于編碼UTF-8、UTF-16、UTF-32等是針對(duì)Unicode字符集的編碼,UCS-2和UCS-4是針對(duì)UCS字符集的編碼(只是我們習(xí)慣把Unicode字符集編碼簡(jiǎn)稱(chēng)為Unicode編碼,把UCS字符集編碼稱(chēng)為UCS編碼)。
Unicode沿用UCS字符集,在UCS-2和UCS-4基礎(chǔ)上提出的UTF-16、UTF-32。并發(fā)展了UTF-8,發(fā)展到現(xiàn)在,就密不可分了,大家基于UCS就把Uniocde維護(hù)好就行,發(fā)布標(biāo)準(zhǔn)大家統(tǒng)一。以往的UCS-2和UCS-4概念就默認(rèn)作廢了這樣一個(gè)關(guān)系,整個(gè)他們的發(fā)展長(zhǎng)話(huà)短說(shuō)就是這樣,懂了嗎。
2、UTF-8、UTF-16、UTF-32、UCS-2、UCS-4對(duì)比:
由于歷史方面的原因,你還會(huì)在不少地方看到把Unicode稱(chēng)為一種編碼的情況,那是因?yàn)?strong>早期的2字節(jié)編碼最初稱(chēng)為“ Unicode”,但現(xiàn)在稱(chēng)為“ UCS-2”,這種情況下的 Unicode 通常就是 UTF-16 或者是更早的 UCS-2 編碼,只是被一直搞混了,在某些老軟件上尤為常見(jiàn)。比如下面editplus里面文件編碼設(shè)置。
以前的Windows電腦上的記事本(左邊)顯示的是Unicode,不過(guò)現(xiàn)在好像改了變成了UTF-16。
不過(guò)由于各種原因,必須承認(rèn),在不同的語(yǔ)境下,“Unicode”這個(gè)詞有著不同的含義。
它可能指:
(1)Unicode 標(biāo)準(zhǔn)
(2)Unicode 字符集
(3)Unicode 的抽象編碼(編號(hào)),也即碼點(diǎn)、碼位(code point)
(4)Unicode 的一個(gè)具體編碼實(shí)現(xiàn),通常即為變長(zhǎng)的 UTF-16(16 或 32 位),又或者是更早期的定長(zhǎng) 16 位的 UCS-2
所以像我一般有時(shí)候非要區(qū)分的話(huà)都是直接說(shuō)全,Unicode 標(biāo)準(zhǔn),Unicode 字符集,Unicode編碼等等。
ANSI編碼
為使計(jì)算機(jī)支持更多語(yǔ)言,通常使用0x800~xFF范圍的2個(gè)字節(jié)來(lái)表示1個(gè)字符。比如:漢字‘中’ 在中文操作系統(tǒng)中,使用 [0xD6,0xD0]這兩個(gè)字節(jié)存儲(chǔ)。
不同的國(guó)家和地區(qū)制定了不同的標(biāo)準(zhǔn),由此產(chǎn)生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的編碼標(biāo)準(zhǔn)。這些使用多個(gè)字節(jié)來(lái)代表一個(gè)字符的各種語(yǔ)言延伸編碼方式,稱(chēng)為 ANSI 編碼。
在簡(jiǎn)體中文Windows操作系統(tǒng)中,ANSI 編碼代表 GBK 編碼;在繁體中文Windows操作系統(tǒng)中,ANSI編碼代表Big5;在日文Windows操作系統(tǒng)中,ANSI 編碼代表 Shift_JIS 編碼。
不同 ANSI 編碼之間互不兼容,當(dāng)信息在國(guó)際間交流時(shí),無(wú)法將屬于兩種語(yǔ)言的文字,存儲(chǔ)在同一段 ANSI 編碼的文本中。
在使用ANSI編碼支持多語(yǔ)言階段,每個(gè)字符使用一個(gè)字節(jié)或多個(gè)字節(jié)來(lái)表示(MBCS,Multi-Byte Character System),因此,這種方式存放的字符也被稱(chēng)作多字節(jié)字符。比如,“中文123” 在中文 Windows 95 內(nèi)存中為7個(gè)字節(jié),每個(gè)漢字占2個(gè)字節(jié),每個(gè)英文和數(shù)字字符占1個(gè)字節(jié)。
在非 Unicode 環(huán)境下,由于不同國(guó)家和地區(qū)采用的字符集不一致,很可能出現(xiàn)無(wú)法正常顯示所有字符的情況。微軟公司使用了代碼頁(yè)(Codepage)轉(zhuǎn)換表的技術(shù)來(lái)過(guò)渡性的部分解決這一問(wèn)題,即通過(guò)指定的轉(zhuǎn)換表將非Unicode 的字符編碼轉(zhuǎn)換為同一字符對(duì)應(yīng)的系統(tǒng)內(nèi)部使用的Unicode 編碼。
可以在“語(yǔ)言與區(qū)域設(shè)置”中選擇一個(gè)代碼頁(yè)作為非 Unicode 編碼所采用的默認(rèn)編碼方式,如936為簡(jiǎn)體中文GBK,950為正體中文Big5(皆指PC上使用的)。在這種情況下,一些非英語(yǔ)的歐洲語(yǔ)言編寫(xiě)的軟件和文檔很可能出現(xiàn)亂碼。而將代碼頁(yè)設(shè)置為相應(yīng)語(yǔ)言中文處理又會(huì)出現(xiàn)問(wèn)題,這一情況無(wú)法避免。
從根本上說(shuō),完全采用統(tǒng)一編碼才是解決之道,雖然現(xiàn)在Unicode有了,但由于歷史遺留,老軟件等等原因,所以系統(tǒng)統(tǒng)一用某種編碼格式的Unicode目前尚無(wú)法做到這一點(diǎn)。
代碼頁(yè)技術(shù)現(xiàn)在廣泛為各種平臺(tái)所采用。UTF-7 的代碼頁(yè)是65000,UTF-8的代碼頁(yè)是65001。簡(jiǎn)體中文上使用的代碼頁(yè)為936,GBK編碼。
以前中文DOS、中文/日文Windows95/98時(shí)代系統(tǒng)內(nèi)碼使用的是ANSI編碼(本地化,根據(jù)不同地區(qū)設(shè)置不同的系統(tǒng)內(nèi)碼Windows版本),現(xiàn)在win7,win10等等系統(tǒng)的內(nèi)碼都是用的Unicode。
不過(guò)微軟為了以前的程序兼容性,比如在某些情況下,比如你的程序需要和不支持Unicode的程序交互時(shí),可能還是會(huì)需要用到code page,提供代碼頁(yè)服務(wù)(就好比微軟不能說(shuō):“老子支持unicode了,以后不支持Unicode的程序都給我滾粗。”只能撅著屁股讓這些老掉牙的程序仍然可以運(yùn)行,于是只好給他們提供一個(gè)“非Unicode默認(rèn)字符集”) 。可以在cmd下輸入chcp查看code page。
WindowsAPI 的Wide Char 表達(dá)是 UTF-16: Unicode (Windows), L 表示是轉(zhuǎn)換為 wide char。
Cocoa的NSString 和 Core Foundation 的CFString 內(nèi)部表達(dá)都是 UTF-16,所以其實(shí) OSX 和 iOS 內(nèi)部處理都用的是 UTF-16。
JavaString 的內(nèi)部表達(dá)是 UTF-16,所以大量跨平臺(tái)程序和 Android 程序其實(shí)內(nèi)部也在用 UTF-16。
大部分的操作系統(tǒng)和 UI framework 的內(nèi)部字符串表達(dá)(內(nèi)碼)都是UTF-16,不過(guò)Linux系統(tǒng)內(nèi)使用的內(nèi)碼是UTF-8。
Tip:內(nèi)碼和外碼
在計(jì)算機(jī)科學(xué)及相關(guān)領(lǐng)域當(dāng)中,內(nèi)碼指的是“將信息編碼后,透過(guò)某種方式存儲(chǔ)在特定記憶設(shè)備時(shí),設(shè)備內(nèi)部的編碼形式”。在不同的系統(tǒng)中,會(huì)有不同的內(nèi)碼。
在以往的英文系統(tǒng)中,內(nèi)碼為ASCII。在繁體中文系統(tǒng)中,當(dāng)前常用的內(nèi)碼為大五碼。在簡(jiǎn)體中文系統(tǒng)中,內(nèi)碼則為國(guó)標(biāo)碼。
為了軟件開(kāi)發(fā)方便,如國(guó)際化與本地化,現(xiàn)在許多系統(tǒng)會(huì)使用Unicode做為內(nèi)碼,常見(jiàn)的操作系統(tǒng)Windows、Mac OS X、Linux皆如此。許多編程語(yǔ)言也采用Unicode為內(nèi)碼,如Java、Python3。
外碼:除了內(nèi)碼,皆是外碼。要注意的是,源代碼編譯產(chǎn)生的目標(biāo)代碼文件(如果Java可執(zhí)行文件或class文件)中的編碼方式屬于外碼。
引用文章:
1、必讀,真說(shuō)清的話(huà)得結(jié)合上面的維基百科上的說(shuō)法理解:
https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
https://en.wikipedia.org/wiki/Unicode
https://en.wikipedia.org/wiki/UTF-16
https://en.wikipedia.org/wiki/UTF-32
2、除此之外參考引用了下面三篇文章的一些觀(guān)點(diǎn):
http://www.blogjava.net/zhenandaci/archive/2008/12/24/248014.html
https://www.cnblogs.com/malecrab/p/5300503.html
https://blog.51cto.com/polaris/377468
原文鏈接:
https://blog.csdn.net/weixin_43465312/article/details/105918985
總結(jié)
以上是生活随笔為你收集整理的万字长文讲解编码知识,看这文就够了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SRM系统排名,国内做srm系统的公司
- 下一篇: 中小型会议如何进行数字化升级?