在前一篇的最后,,留下了一個(gè)問(wèn)題,,即string.getBytes(“UTF-16”)會(huì)在開頭多出兩個(gè)字節(jié)”FEFF”來(lái),Unicode中稱之為BOM,,接下來(lái)就讓我們一起來(lái)了解有關(guān)BOM的知識(shí),,在此之前我們需要說(shuō)說(shuō)有關(guān)端法的知識(shí)。 什么是端法(endian),?在具體介紹它之前,,讓我們先看看雞蛋的兩種擺法: 大家看出區(qū)別來(lái)了嗎?上面的一排都是尖的一端(或者說(shuō)小端)向著左,,較圓的一端(或者說(shuō)大端)向著右,;而下面一排正好相反。
如果按照我們從左到右的習(xí)慣認(rèn)為左是前面,上面可以說(shuō)是小端在前,,而下面的則是大端在前,。有人可能要問(wèn),這與我們的BOM有何關(guān)系,?我們知道UTF-16一個(gè)代碼單元有兩個(gè)字節(jié),,如果把一代碼單元比作一個(gè)雞蛋,那么它也有兩個(gè)端,,一個(gè)字節(jié)是小端,,另一個(gè)則是大端。 大端法(Big endian)以兩個(gè)UTF-16的編碼0x0048與0x4F60為例,,如果我們把它們書寫成 00 48 4F 60,,這樣對(duì)我們而言也是非常自然的一種方式,00與4F都屬于高位,我們又常常說(shuō)“高大高大”的,,高與大總是關(guān)系緊密,,自然這樣一種高位在前的方式就是大端法(Big endian)了。
那么,,自然的,與大端法相反的那種就是小端法了,。 小端法(Little endian)還是以兩個(gè)UTF-16的編碼0x0048與0x4F60為例,,如果我們把它們書寫成 48 00 60 4F,那么這樣一種低位在前的方式就是小端法(Little endian)了,。
我估計(jì)很多人會(huì)有些疑問(wèn),為什么弄出這么一種很“不自然”很別扭的方式來(lái)呢,?請(qǐng)注意,,我給很不自然里的“不自然”三個(gè)字打了引號(hào),而且你可能也注意到了我在前面強(qiáng)調(diào)了“書寫”兩字,,其實(shí)呢,大小端法應(yīng)該是從存儲(chǔ)層面考慮的,,在此之前讓我們看看我們是如何看待內(nèi)存布局的,。 內(nèi)存中的存儲(chǔ)如果有一排的格子來(lái)表示內(nèi)存,我們來(lái)給它們編號(hào)(其實(shí)就是地址),,那么自然按照從左到右的習(xí)慣,,地址編號(hào)越來(lái)越大,下面是一個(gè)四字節(jié)內(nèi)存的示意圖: 那么現(xiàn)在把大端法表示的四字節(jié)放進(jìn)去,,結(jié)果如下: 那么我們發(fā)現(xiàn),,就單個(gè)編碼而言,高位的字節(jié)反而放到了低地址上,,而低位的字節(jié)卻放到了高地址上:
這么下來(lái),我們所謂很“自然”的大端法反而有點(diǎn)不自然了,。 與此相反,,讓我們把小端法放入內(nèi)存,,結(jié)果如下: 那么,與大端法相反,,現(xiàn)在它的高低字節(jié)反而與地址的高低位自然地對(duì)應(yīng)上了,。
需要強(qiáng)調(diào)的是,,所謂大小端僅僅是字節(jié)間的關(guān)系,
每個(gè)單獨(dú)字節(jié)里的8個(gè)位依然還是高位在前,,無(wú)論大小端均是如此,。下圖是小端法單個(gè)字節(jié)內(nèi)部以二進(jìn)制表示的示意圖:
大小端法的來(lái)歷關(guān)于Big endian和Little endian,它們是有來(lái)頭的,,下面文字引自阮一峰的網(wǎng)絡(luò)日志http://www./blog/2007/10/ascii_unicode_and_utf-8.html
所以你明白了為何前面先畫了幾個(gè)雞蛋來(lái)示意,。我們當(dāng)然不會(huì)無(wú)聊到為雞蛋從哪頭敲開去打仗,不過(guò)關(guān)于端法哪種好也是爭(zhēng)論不休的,,在前面我們就談到了哪種更“自然”的問(wèn)題,。 端法與系統(tǒng)架構(gòu)在Windows平臺(tái)下,當(dāng)使用記事本程序保存文件時(shí),編碼里有幾個(gè)選項(xiàng): 可以看到一個(gè)“Unicode”和“Unicode big endian”,,通過(guò)以上名稱的對(duì)比及對(duì)大端法的特別標(biāo)示,,我們可以猜測(cè)出,Windows下缺省是小端法,。
Windows平臺(tái)為何使用小端法呢,?說(shuō)起來(lái)與CPU制造商英特爾(Intel)又有很大關(guān)系。
內(nèi)存(Memory)中使用端法其實(shí)又是受到寄存器(Register)中使用的端法的影響,,因?yàn)閮烧咧g經(jīng)常要來(lái)回拷貝數(shù)據(jù)。英特爾的CPU就使用了小端法,。 需要強(qiáng)調(diào)的另一點(diǎn)是,,雖然我們討論是字符集編碼,但端法并不限于此,,還可以是其它,,比如一個(gè)int,通常是4字節(jié),,以一個(gè)整數(shù)0x0A0B0C0D為例(以下圖片來(lái)自wiki的截圖): 除此之外,,圖片文件中也可能會(huì)涉及端法的問(wèn)題,網(wǎng)絡(luò)傳輸中也同樣有端法的問(wèn)題,,對(duì)此有興趣的可以參考http://en./wiki/Endianness,。 至于為何英特爾采用了小端法,而其它一些廠商又使用了大端法以及這兩種端法到底哪種好等問(wèn)題,,這里就不打算深入下去了,,總之,大家知道這個(gè)世界比較亂就是了,,有興趣有精力的同學(xué)可以自行搜索以了解更多。
回到編碼的問(wèn)題,,在記事本中以ANSI之外的三種編碼分別保存一下“hello你好”,,分別命名為UTF16BE.txt,UTF16.txt,,UTF8.txt(分別對(duì)應(yīng)“Unicode big endian”,,“Unicode”,“UTF-8”) 以16進(jìn)制查看一下: 什么是BOM,?BOM=Byte Order Mark,,翻譯過(guò)來(lái)就是“字節(jié)順序標(biāo)識(shí)”,也即是上圖中紅色框中的部分,。
從上圖中可以看出:
UTF-32的BOM雖然前面一直在說(shuō)的是UTF-16,,但UTF-32同樣也有BOM,以下代碼是一些測(cè)試:
不過(guò),,有點(diǎn)遺憾的是,,UTF-32在本機(jī)測(cè)試時(shí),缺省情況下也沒(méi)有BOM輸出,,這點(diǎn)與UTF-16的情況又不同:
下圖是各種BOM的一個(gè)匯總(圖片截取自u(píng)nicode.org): BOM與碼點(diǎn)U+FEFFBOM其實(shí)就是U+FEFF這一碼點(diǎn),“EF BB BF”就是這一碼點(diǎn)在UTF-8下的編碼,。代碼如下: U+FEFF稱為“zero-width non-breaking space”
縮寫成“ZWNBSP”,,如下圖所示: 在用作BOM之后,,Unicode不再建議這樣去解釋(deprecated),而是建議用U+2060來(lái)代替,,U+FEFF就作為BOM的專用,。 U+2060稱為“Word Joiner”(字面義:詞連接器),縮寫為“WJ”,,如下圖所示: UTF-8的BOM從前面測(cè)試可知,,java中,UTF-8缺省不帶BOM,,這點(diǎn)與記事本又不同:
另:UTF-8也不存在所謂的大小端兩種情況,,統(tǒng)一為大端法,,BOM僅僅作為一種所用編碼的指示。
UTF-8中的BOM已經(jīng)偏離了它的本意,而這一點(diǎn)估計(jì)也是Unicode組織不推薦在UTF-8中使用BOM的一大原因,。
總結(jié)其實(shí)關(guān)于端法及BOM并沒(méi)有太多好說(shuō)的,,通常大家知道有這么一回事或者說(shuō)有那么一些“坑”也就夠了,關(guān)于BOM的話題就談到這里,。 |
|