久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

深入圖解字符集與字符集編碼(七)

 flysnowxg 2014-12-06

在前一篇的最后,,留下了一個(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),?


在具體介紹它之前,,讓我們先看看雞蛋的兩種擺法:

image_thumb18

大家看出區(qū)別來(lái)了嗎?上面的一排都是尖的一端(或者說(shuō)小端)向著左,,較圓的一端(或者說(shuō)大端)向著右,;而下面一排正好相反。

畫得不咋的,,大家湊合看就是了,,畫出了《蒙娜麗莎》(Mona Lisa)的達(dá)芬奇(Leonardo da Vinci)據(jù)說(shuō)開始學(xué)畫畫時(shí)也畫過(guò)一段時(shí)間雞蛋呢,有說(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)存的示意圖:

image

那么現(xiàn)在把大端法表示的四字節(jié)放進(jìn)去,,結(jié)果如下:

image

那么我們發(fā)現(xiàn),,就單個(gè)編碼而言,高位的字節(jié)反而放到了低地址上,,而低位的字節(jié)卻放到了高地址上:

如上,,高位的字節(jié)00放到了低地址0x0000上,低位的字節(jié)48卻放到了高地址0x0001上,。4F60的情況也與此類似,。

這么下來(lái),我們所謂很“自然”的大端法反而有點(diǎn)不自然了,。

與此相反,,讓我們把小端法放入內(nèi)存,,結(jié)果如下:

image

那么,與大端法相反,,現(xiàn)在它的高低字節(jié)反而與地址的高低位自然地對(duì)應(yīng)上了,。

所以呢,我們前面說(shuō)小端“不自然”,,那是對(duì)書寫時(shí)的情況而言,,考慮到存儲(chǔ)層面,它看上去倒似乎更自然了,。

需要強(qiáng)調(diào)的是,,所謂大小端僅僅是字節(jié)間的關(guān)系

這也暗示了只有多字節(jié)情況才會(huì)有所謂的端法,,而通常又在偶數(shù)字節(jié)情況下更為普遍,,如UTF-16,UTF-32,,這樣才能更好分出“兩個(gè)端”來(lái),。下面談到UTF-8時(shí)將會(huì)再度闡述這一問(wèn)題。

每個(gè)單獨(dú)字節(jié)里的8個(gè)位依然還是高位在前,,無(wú)論大小端均是如此,。下圖是小端法單個(gè)字節(jié)內(nèi)部以二進(jìn)制表示的示意圖:

image

當(dāng)然了,建立在字節(jié)抽象層面上的操作已經(jīng)無(wú)需關(guān)注字節(jié)內(nèi)部究竟是什么端法了,,甚至已經(jīng)不存在端法這一說(shuō)法了,。

大小端法的來(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)自英國(guó)作家斯威夫特的《格列佛游記》,。在該書中,小人國(guó)里爆發(fā)了內(nèi)戰(zhàn),,戰(zhàn)爭(zhēng)起因是人們爭(zhēng)論,,吃雞蛋時(shí)究竟是從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。為了這件事情,,前后爆發(fā)了六次戰(zhàn)爭(zhēng),,一個(gè)皇帝送了命,另一個(gè)皇帝丟了王位,。

所以你明白了為何前面先畫了幾個(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):

image

可以看到一個(gè)“Unicode”和“Unicode big endian”,,通過(guò)以上名稱的對(duì)比及對(duì)大端法的特別標(biāo)示,,我們可以猜測(cè)出,Windows下缺省是小端法,。

注:關(guān)于這里的Unicode,,前面篇章中也有提及,實(shí)際就是UTF-16編碼,。

Windows平臺(tái)為何使用小端法呢,?說(shuō)起來(lái)與CPU制造商英特爾(Intel)又有很大關(guān)系。

這兩者我們又常叫它們?yōu)閃intel聯(lián)盟,。(Wintel=Windows+Intel)

內(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的截圖):

image

除此之外,,圖片文件中也可能會(huì)涉及端法的問(wèn)題,網(wǎng)絡(luò)傳輸中也同樣有端法的問(wèn)題,,對(duì)此有興趣的可以參考http://en./wiki/Endianness,。

至于為何英特爾采用了小端法,而其它一些廠商又使用了大端法以及這兩種端法到底哪種好等問(wèn)題,,這里就不打算深入下去了,,總之,大家知道這個(gè)世界比較亂就是了,,有興趣有精力的同學(xué)可以自行搜索以了解更多。

在2010年央視春晚小品《捐助》中,,趙本山的徒弟王小利扮演的親家說(shuō):“他刨的不深,,我要往祖墳上刨。”針對(duì)端法的爭(zhēng)論,,這里就不打算往祖墳上刨了,,另一方面的原因是刨不動(dòng)了,再刨就到硬件層面上去了,,所以呢,,非不為也,是不能也!

回到編碼的問(wèn)題,,在記事本中以ANSI之外的三種編碼分別保存一下“hello你好”,,分別命名為UTF16BE.txt,UTF16.txt,,UTF8.txt(分別對(duì)應(yīng)“Unicode big endian”,,“Unicode”,“UTF-8”)

以16進(jìn)制查看一下:

image

什么是BOM,?


BOM=Byte Order Mark,,翻譯過(guò)來(lái)就是“字節(jié)順序標(biāo)識(shí)”,也即是上圖中紅色框中的部分,。

自然地,,這里所謂的字節(jié)順序其實(shí)就是指使用了哪種端法。

從上圖中可以看出:

  • UTF-16 BE(Big Endian)的BOM是:FE FF

  • UTF-16 LE(Little Endian)的BOM是:FF FE

  • UTF-8的BOM是:EF BB BF

注:前面說(shuō)到,,getBytes(“UTF-16”)得到的缺省BOM是“FEFF”,,可見JVM中缺省是大端法,這與Windows平臺(tái)下缺省為小端法恰好相反,。

UTF-32的BOM

雖然前面一直在說(shuō)的是UTF-16,,但UTF-32同樣也有BOM,以下代碼是一些測(cè)試:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package org.jcc.core.encode;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static org.assertj.core.api.Assertions.assertThat;
public class BOMTest {
     
    @Test
    public void testBOM() throws UnsupportedEncodingException {
        String s = "hello你好";
        // ============================ UTF-16
        // java中的缺省是大端
        assertThat(printHexBinary(s.getBytes("UTF-16"))).isEqualTo("FEFF00680065006C006C006F4F60597D");
        // 注意:特別指明端法后的字節(jié)數(shù)組將不再帶上BOM
        assertThat(printHexBinary(s.getBytes("UTF-16BE"))).isEqualTo("00680065006C006C006F4F60597D");
        // 小端法表示的字節(jié)數(shù)組需要特別使用"UTF-16LE"來(lái)獲取
        assertThat(printHexBinary(s.getBytes("UTF-16LE"))).isEqualTo("680065006C006C006F00604F7D59");
        // ============================ UTF-8
        // java中,,UTF-8缺省不帶BOM,,這點(diǎn)與記事本又不同
        // 另:UTF-8不存在所謂的大小端,全部是大端,,BOM僅僅作為一種所用編碼的指示
        assertThat(printHexBinary(s.getBytes("UTF-8"))).isEqualTo("68656C6C6FE4BDA0E5A5BD");
         
         
        // ============================ UTF-32
        // UTF-32太長(zhǎng),,用短一點(diǎn)的串來(lái)測(cè)試
        String s32 = "he";
        // 注意:在本機(jī)測(cè)試時(shí),缺省情況下也沒(méi)有BOM(不同的虛擬機(jī)實(shí)現(xiàn)可能會(huì)不一樣?。,。?/code>
        assertThat(printHexBinary(s32.getBytes("UTF-32"))).isEqualTo("0000006800000065");
        // 為防止意外,最好明確設(shè)置
        assertThat(printHexBinary(s32.getBytes("UTF-32BE"))).isEqualTo("0000006800000065");
        assertThat(printHexBinary(s32.getBytes("UTF-32LE"))).isEqualTo("6800000065000000");
    }
}

不過(guò),,有點(diǎn)遺憾的是,,UTF-32在本機(jī)測(cè)試時(shí),缺省情況下也沒(méi)有BOM輸出,,這點(diǎn)與UTF-16的情況又不同:

但以上僅是在本機(jī)測(cè)試的情況,,請(qǐng)不要將此作為一個(gè)結(jié)論,不同的虛擬機(jī)實(shí)現(xiàn)可能會(huì)不一樣??!

下圖是各種BOM的一個(gè)匯總(圖片截取自u(píng)nicode.org):

image

BOM與碼點(diǎn)U+FEFF

BOM其實(shí)就是U+FEFF這一碼點(diǎn),“EF BB BF”就是這一碼點(diǎn)在UTF-8下的編碼,。代碼如下:

1
2
3
4
5
    @Test
    public void testBomCodePoint() throws UnsupportedEncodingException {
        String s = "\uFEFF";
        assertThat(printHexBinary(s.getBytes("UTF-8"))).isEqualTo("EFBBBF");
    }

U+FEFF稱為“zero-width non-breaking space”

字面義:零寬度非換行空格,。也即碰到時(shí)把它解釋成這樣,,顯示上的實(shí)際效果就是啥也沒(méi)顯示。

縮寫成“ZWNBSP”,,如下圖所示:

image

在用作BOM之后,,Unicode不再建議這樣去解釋(deprecated),而是建議用U+2060來(lái)代替,,U+FEFF就作為BOM的專用,。

U+2060稱為“Word Joiner”(字面義:詞連接器),縮寫為“WJ”,,如下圖所示:

image

UTF-8的BOM

從前面測(cè)試可知,,java中,UTF-8缺省不帶BOM,,這點(diǎn)與記事本又不同:

按Unicode組織的說(shuō)法,,UTF-8可帶可不帶BOM,不作強(qiáng)制要求,,但不推薦用BOM,,原因之一是為與ASCII的兼容。

另:UTF-8也不存在所謂的大小端兩種情況,,統(tǒng)一為大端法,,BOM僅僅作為一種所用編碼的指示。

UTF-8中有一字節(jié)的情況,,這種情況,,就沒(méi)有兩端的說(shuō)法了。至于另外的二,,三,,四字節(jié)情況,以三字節(jié)為例,,如果你一定要弄出端法,,也不是說(shuō)不可以,比如,,小端法就是“小-中-大”,,大端法就是“大-中-小”。但現(xiàn)實(shí)情況是UTF-8僅僅采用了一種端法,,就是大端法,。

UTF-8中的BOM已經(jīng)偏離了它的本意,而這一點(diǎn)估計(jì)也是Unicode組織不推薦在UTF-8中使用BOM的一大原因,。

在eclipse中,以UTF-8保存時(shí)就沒(méi)有BOM,,但它的編輯器也能正確處理帶BOM的情況,。(這個(gè)世界還真有點(diǎn)亂~

總結(jié)

其實(shí)關(guān)于端法及BOM并沒(méi)有太多好說(shuō)的,,通常大家知道有這么一回事或者說(shuō)有那么一些“坑”也就夠了,關(guān)于BOM的話題就談到這里,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多