網頁中的編碼與亂碼(4) 原

這一篇將介紹 BOM 在 html 頁面編碼中的運用。在最前面曾提到,它的優先級實際上是最高的,在這裏,將具體介紹什麼是 BOM,還會解析爲什麼它的優先級最高,然後還會構建一些具體的測試來驗證這一點。

什麼是 BOM?

關於什麼是 BOM,在這篇文章中有詳細的介紹:

字符集與編碼(七)——BOM

這裏也稍微囉嗦幾句,內容也基本出自上述文章:BOM=Byte Order Mark,翻譯過來就是“字節順序標識”。

具體則分爲兩種:小端序Little endian)和大端序Big endian)。

我們知道,在記事本中“另存爲”時可以選擇編碼,有以下幾種:

記事本 另存爲 編碼選擇

這裏的 Unicode 實際就是 UTF-16(小端序).

注:Java 平臺中 UTF-16 缺省爲 大端序,與 Windows 恰好相反。

另:記事本的 UTF-8 默認是帶 BOM 的,而多數 IDE 的編輯器 UTF-8 默認不帶 BOM。

在記事本中以 ANSI 之外的三種編碼分別保存一下“hello你好”,分別命名爲UTF16BE.txt,UTF16.txt,UTF8.txt(分別對應“Unicode big endian”,“Unicode”,“UTF-8”),查看三個文件的十六進制形式,結果是這樣的:

utf16 utf8 bom unicode big endian 大端序 小端序 示例

這三個文件中最前面紅框中的字節就是所謂的”BOM“,它不是具體內容”hello你好“的一部分,而是一個額外的特徵值。

從上圖的紅色箭頭不難發現,UTF-16 大小端之間,兩兩字節間順序恰好相反。對於 UTF-16 字節流而言,開頭的 BOM 是必須的,因爲它指示了字節流到底是大端序還是小端序,如果弄反了,整個字節流的內容就會面目全非。

而對於 UTF-8 而言呢?實際上卻不存在字節序,或者說它只有大端序,就一種字節序。所以,對 UTF-8 而言,BOM 是可以省略的,對於它而言,我們常說的它是“帶 BOM”還是“不帶 BOM”,而不是說它到底是什麼端序。更多時候它的 BOM 是作爲字符集編碼的標誌,因爲只要看到這樣的 BOM,就知道它是 UTF-8 編碼。

下圖是各種 BOM 的一個彙總(圖片截取自unicode.org):

BOM 彙總 utf8 utf16 utf32

BOM 其實就是 U+FEFF 這一碼點,“EF BB BF”就是這一碼點在 UTF-8 下的編碼。測試代碼如下:

utf8 bom 測試代碼

爲什麼 BOM 的優先級最高?

顯然,BOM 其實類似於文檔內編碼聲明,不同之處則在於它是由文本編輯器控制寫入的,而且是置於文檔的最開頭處。

假如說你選擇了保存爲 UTF-8 帶 BOM 的編碼,文本編輯器就會在內容之前添加“EF BB BF”三個字節,然後之後的內容也一定是用 UTF-8 來編碼的。不會發生在“文檔內編碼聲明”中那種錯誤聲明的情況。

這種一致性是由編輯器爲我們保證的,除非你直接修改那些最終的二進制的值,否則這種宣稱的 BOM 與實際所用的編碼的一致性是能得到嚴格保證的。

換言之,就是它的置信度是最好的,所以它的優先級最高也就不難理解了。

帶 BOM 的靜態頁面測試

如下構建了一個帶 BOM 的靜態頁面,

utf8 bom html 頁面

下方編碼處的值“UTF-8-BOM”表明了它的實際編碼,頁面還故意帶了一個錯誤的 meta 聲明。

Eclipse 的 編輯器不支持把 UTF-8 保存爲帶 BOM 的,這個需要用記事本或 notepad++ 這樣的編輯器來完成。不過 Eclipse 中的編輯器也能正常識別帶 BOM 的 UTF-8 編碼的頁面。

header 也故意用 gbk 編碼來添亂:

mime mapping text/html charset gbk

但由於 BOM 的優先級最高,chrome 瀏覽器還是能正常打開:

utf8 bom 頁面 瀏覽器 測試

不過在 IE 下的測試卻沒有通過,它還是採用了 header 中建議的值來解析,從而導致了亂碼:

utf8 bom 頁面 IE瀏覽器 測試

但是像 Firefox,Edge 這些較爲現代的瀏覽器都能正常:

utf8 bom 頁面 firefox edge 瀏覽器 測試

表明它們都遵循了 BOM 優先的建議,IE 則是個例外。

這也表明了事情可能要比我們想象的要複雜。

帶 BOM 的動態響應測試

同理,也不難構建一個動態的帶 BOM 的 UTF-8 響應,同樣設置了很多的干擾因素:

utf8 bom 動態頁面 示例代碼

前面說到:BOM 其實就是 U+FEFF.

瀏覽器還是能夠通過測試:

utf8 bom 動態頁面 瀏覽器測試

用 UTF-16 也是可以的:

utf16 bom 動態頁面 示例代碼

注意:getBytes(“utf-16”) 生成的字節流數組會自動帶上 BOM,這裏不需要手動像 UTF-8 那樣添加 BOM。前面也說了,對 utf-16 來說,BOM 是必不可少的。

測試也 OK:

utf16 bom 動態頁面 瀏覽器測試

不過如果你明確寫成 getBytes(“utf-16le”) 或 getBytes(“utf-16be”),  那麼生成的字節流數組就不會帶上 BOM 了,這時你要自己在前面加入 BOM,像 UTF-8 時那樣:

utf16le bom 動態頁面 示例代碼

結果也是 OK 的:

utf16le bom 動態頁面 瀏覽器測試

你可以始終用那個轉義的 \uFEFF 來表示 BOM,但如果你決定手動輸出字節(圖上註釋代碼部分),就要注意小端序時的順序,否則輸出結果將會是面目全非。

關於 BOM 編碼的介紹就到這裏,最後一篇將談談剩下的最後一種情況,也即是“缺省”情況,並最終做個總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章