上篇主要了解了字符集和字符集編碼的相關知識,其中有提到位元組序的問題,這篇我們便深入探討下這方面的知識。
位元組序
位元組順序,又稱端序或尾序(英語:Endianness)。在電腦科學領域中,是跨越多位元組的程式物件的儲存規則。在幾乎所有的機器上,多位元組物件都被儲存為連續的位元組序列。例如在Java中,一個int型別的變數a地址為0x100,且x
的四個位元組將被儲存在儲存器的0x100, 0x101, 0x102, 0x103
位置。而儲存地址內的排列則有兩個通用規則。一個多位的整數將按照其儲存地址的最低或最高位元組排列。如果最低有效位在最高有效位的前面,則稱小端序;反之則稱大端序。在網路應用中,位元組序是一個必須被考慮的因素,因為不同機器型別可能採用不同標準的位元組序,所以均按照網路標準轉化。
例如假設上述變數x
型別為int
,位於地址0x100
處,它的十六進位制為0x01234567
,地址範圍為0x100~0x103
位元組,其內部排列順序依賴於機器的型別。大端法從首位開始將是:0x100: 01, 0x101: 23,..
。而小端法將是:0x100: 67, 0x101: 45,..
。
大端序
高位位元組在前,低位位元組在後,這是人類習慣的讀寫數值方法。以一個值為0x0A0B0C0D
的int型別變數為例,它的記憶體為0x100~0x103,則:
記憶體地址 | 0x100 | 0x101 | 0x102 | 0x103 |
---|---|---|---|---|
儲存單元 | 0x0A | 0X0B | 0X0C | 0X0D |
示例中,最高位位元組是0x0A 儲存在最低的記憶體地址處。下一個位元組0x0B存在後面的地址處。正類似於十六進位制位元組從左到右的閱讀順序。
小端序
低位位元組在前,高位位元組在後,現大部分計算機內部處理都是小端序。同樣以上面做例子:
記憶體地址 | 0x100 | 0x101 | 0x102 | 0x103 |
---|---|---|---|---|
儲存單元 | 0x0D | 0X0C | 0X0B | 0X0A |
最低位位元組是0x0D 儲存在最低的記憶體地址處。後面位元組依次存在後面的地址處。
實際意義
到這裡我們可以發現,在日常開發中,如果不熟悉位元組序的情況下,在涉及位元組的讀取和解析容易出現問題,那麼,為什麼要區分大端和小端呢,統一不是更方便嗎? 其實這裡涉及一個效率的問題,計算機電路先處理低位位元組,效率比較高,因為計算都是從低位開始的。如果都是大端序,則計算時需要從高位找到低位,再從低位計算到高位,影響效率。所以,計算機的內部處理都是小端位元組序。但是我們人類還是習慣讀寫大端位元組序。所以,除了計算機的內部處理,其他的場合幾乎都是大端位元組序,比如網路傳輸和檔案儲存。
計算機處理位元組序的時候,不知道什麼是高位位元組,什麼是低位位元組。它只知道按順序讀取位元組,先讀第一個位元組,再讀第二個位元組。**如果是大端位元組序,先讀到的就是高位位元組,後讀到的就是低位位元組。小端位元組序正好相反。**只有讀取的時候,才必須區分位元組序,其他情況都不用考慮。
BOM
我們知道,UTF-16和UTF-32都是多位元組的編碼規則,那在讀取的時候必然也會涉及到位元組序的問題,計算機是通過什麼判斷編碼之後的位元組序呢,答案就是BOM。
位元組順序標記(byte-order mark,BOM)是位於碼點U+FEFF
的統一碼字元的名稱。當以UTF-16或UTF-32來將UCS/統一碼字元所組成的字串編碼時,這個字元被用來標示其位元組序。它常被用來當做標示檔案是以UTF-8、UTF-16或UTF-32編碼的標記。而 FFFE 在 UCS 中是不存在的字元,所以不會出現在實際傳輸中。UCS 規範建議我們在傳輸位元組流前,先傳輸BOM標記。這樣如果接收者收到 FEFF,就表明這個位元組流是大端序的;如果收到 FFFE,就表明這個位元組流是小端序的。
UTF-8 不需要 BOM 來表明位元組順序,但可以用 BOM 來表明編碼方式。BOM的 UTF-8 編碼是 EF BB BF。所以如果接收者收到以 EF BB BF 開頭的位元組流,就知道這是 UTF-8 編碼了。Windows 就是使用 BOM 來標記文字檔案的編碼方式的。微軟在 UTF-8 中使用 BOM 是因為這樣可以把 UTF-8 和 ASCII 等編碼明確區分開,但這樣的檔案在 Windows 之外的作業系統裡會帶來問題。
最後我們再來對比一下攜帶BOM之後UTF-16編碼所得到的結果,其中UTF-16BE代表大端序,UTF-16LE代表小端序: