IE中打開UTF-8編碼title為中文的網頁會顯示空白頁的問題

(忘了引用來源)

很久很久以前(大概2005年10月 ~2006年3月),當時在blogger.com寫Blog。當時blogger.com有中文界面,對中文用戶也算是比較關心了,不過 blogger.com的所有模版裡都有一個問題,那就是<title>標籤被放在<meta>標籤前面。當title為中文的 時(比如Blog名為中文或者文章標題為中文),在IE下會出現顯示空白頁的問題。昨天Dre·J在群裡又問到這個問題,今天過來好好研究一下。

這個問題只存在於blogger.com中,WordPress系統中不存在。先說一下在blogger.com中這個問題的解決辦法:在模版的<body>標籤下面找到<title>標籤,調整成這樣:

<$BlogMetaData$>
<title><$BlogPageTitle$></title>

保證meta在前面就可以了。可以參考《感謝Yskin》《UTF-8字符集網頁在IE上會顯示空白問題的解決方案》

這個問題要從瀏覽器解析html的方式講起。瀏覽器讀取了頁面的html代碼後開始進行解析。解析前瀏覽器要先知道頁面的編碼方式,然後根據編碼方 式進行解碼,然後才能開始解析。我大概想了一下,瀏覽器可以從下面3個方面得到頁面編碼方式:HTTP Header中的"Content-Type"項、返回的html代碼開頭是否有BOM、html代碼中的meta標籤。

做了一個小測試,使用Windows 2000 SP4操作系統,IE6 SP1和Firefox 1.5.0.5瀏覽器。所有文件使用DOS格式換行符。測試代碼如下:

<?php
header("Content-Type: text/html; charset=utf-8");
?><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>你好啊</title>
</head>
<body>
你好啊。
</body>

先 不要前面的PHP語句,直接使用html文件,分無meta、meta在title前、meta在title後3種方式,分別做成GBK、UTF -8(no BOM)、UTF-8(BOM)三種編碼方式的文件,再分別用IE和Firefox測試。我的Blog所在的服務器上,訪問html文件時HTTP Header裡Content-Type是Content-Type: text/html。第二遍測試加上PHP語句,用Header函數給HTTP Header中加上Content-Type: text/html; charset=utf-8,再把第一遍做的重新做一遍。
IE6 SP1 Firefox 1.5.0.5 字節 地址 無meta meta在前 meta在後 —加了Header語句後— 無meta meta在前 meta在後

GBK 正常 正常 73 t11.html
UTF-8(no BOM) 空白頁 使用GBK解碼形成亂碼 80 t12.html
UTF-8(BOM) 正常 正常 83 t13.html
GBK 使用UTF-8解碼形成亂碼 使用UTF-8解碼形成亂碼 144 t21.html
UTF-8(no BOM) 正常 正常 151 t22.html
UTF-8(BOM) 正常 正常 154 t23.html
GBK 使用UTF-8解碼形成亂碼 使用UTF-8解碼形成亂碼 144 t31.html
UTF-8(no BOM) 空白頁 正常 151 t32.html
UTF-8(BOM) 正常 正常 154 t33.html
GBK 使用UTF-8解碼形成亂碼 使用UTF-8解碼形成亂碼 133 t11.php
UTF-8(no BOM) 正常 正常 140 t12.php
UTF-8(BOM) 正常 正常 143 t13.php
GBK 使用UTF-8解碼形成亂碼 使用UTF-8解碼形成亂碼 204 t21.php
UTF-8(no BOM) 正常 正常 211 t22.php
UTF-8(BOM) 正常 正常 214 t23.php
GBK 使用UTF-8解碼形成亂碼 使用UTF-8解碼形成亂碼 204 t31.php
UTF-8(no BOM) 正常 正常 211 t32.php
UTF-8(BOM) 正常 正常 214 t33.php

文件中有6個漢字和一個漢字句號,所以UTF-8(no BOM)格式比GBK格式多出7個字節。UTF-8的BOM佔用3個字節,所以UTF-8(BOM)比UTF-8(no BOM)多出3個字節。經驗證,所有數據都符合這個規則,所以各文件格式沒有錯誤。

PHP不支持BOM,又因為BOM的3個字符在最前面,顯示不包含在<?php…?>標籤裡,所以PHP引擎會3個字符輸出,於是 輸出的html文件也有了BOM。所以這次測試中,為了修改http header而加入的PHP語句不影響最終輸出的html文件的BOM。

從測試結果可以看出,瀏覽器(無論是IE還是Firefox)在解析頁面時,首先取HTTP Header中的Content-Type項,如果有寫明charset的話就認定頁面的編碼方式為charset指定的值。如果沒有指明,則認定為默認 值。根據上表,IE中文版的默認值是GB2312,Firefox中文版的默認值是GBK,不過IE的GB2312好像和GBK沒啥區別。然後,瀏覽器會 看一下有沒有BOM。一旦發現有UTF-8的3字節BOM,則重新認定頁面的編碼方式為UTF-8。

然後是解碼階段,解碼完成後是解析html的階段。解析html的過程中,當解析到head部分的meta標籤時,瀏覽器會根據<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />這個語句中的說明,重新認定編碼方式為charset後面的方式,中斷html解析過程,返回到解碼步驟重新解碼。

知道了這個步驟,再來看這個表:在加了Header語句設置了HTTP Header後,兩個瀏覽器解析所有頁面都是用的UTF-8方式,包括GBK編碼的頁面。(當然要正常解析GBK編碼的文件,可以在title前加上個 meta標籤標明編碼方式。)在上表的下半部分可以清楚的看到這一點。再來看上半部分,在沒有加Header語句的頁面裡,首先瀏覽器認定頁面編碼方式為 默認值GBK。檢測有無UTF-8的3字節BOM,檢測到的,認定頁面編碼方式為UTF-8,解碼再解析html,一切正常。如上表所示,上半部分帶 BOM的頁面都能正常顯示。如果沒有BOM,頁面可能是GBK或者UTF-8(no BOM)格式,瀏覽器會先按照默認的GBK方式開始解碼。頁面為GBK格式時,無meta時正常,有meta時瀏覽器解析到meta標籤會回頭重現按 UTF-8方式解碼,所以GBK,meta在前或後,無論IE還是FF都是亂碼。再看UTF-8(no BOM)的頁面,無meta時FF用GBK方式解碼下去,最終顯示亂碼,IE則解碼出錯,形成空白頁。有meta時,Firefox找到meta後回頭重 新按UTF-8方式解碼,所以無論meta在前或在後都是正常;IE則是在meta在前時能夠和Firefox一樣回頭重新解碼,當meta在後時,又是 解析到title出錯,返回空白頁。

所以,IE顯示空白頁的問題,很明顯是因為IE的解碼程序兼容性差。上網查了下,GBK的編碼範圍是0×8140-0xfefe。從GB2312- 80開始,因為ASCII碼的範圍是0~127,首字位是0,所以GB2312-80使用雙字節,並設置首字位為1。「GBK 亦採用雙字節表示,總體編碼範圍為 8140-FEFE,首字節在 81-FE 之間,尾字節在 40-FE 之間。」[via]UTF-8中中文都是3個字節的,由於Unicode中中日韓的文字都混在一起,可以使用Windows自帶的字符映射表查看CJK表意字符的範圍,即為漢字的範圍。(可以參考我的這篇文章裡的圖片)3字節的UTF-8編碼是這樣子的:1110xxxx 10xxxxxx 10xxxxxx,編碼範圍是8000-EFFF,首字節在80-EF之間,尾字節在00-FF之間。[via] 顯然當一段UTF-8編碼的文本被按照GBK方式解碼的時候,由於有一些編碼在GBK中不存在,造成解碼程序出現錯誤。當UTF-8文本被按照GBK的方 式解碼的時候,前兩個字節會被認為是一個字,後一個字節將和下一個字符結合。當<title>標籤裡的漢字數是偶數個時,勉強有3/4的概率 通過解碼程序(因為GBK的第二個字節要求是40-FE),當有奇數個漢字的時候,最後一個漢字的三個字節的最後一個字節會和< /title>的第一個字符<結合,而<的編碼是3C,正好不在尾字節40-FE的範圍中,造成錯誤。如果< /title>標籤前有多餘的空格也會產生錯誤,因為空格的編碼20也不在範圍中。

再做一個測試,修改t12.html中title的部分,修改成兩個漢字,再用IE打開發現不再是空白頁,而是用GBK編碼導致的亂碼頁面了。地址:s.html

所以,空白頁的出現是不固定的。《UTF-8字符集網頁在IE上會顯示空白問題的解決方案》所說的「IE 解析網頁編碼時是 HTML 內的標識優先的,然後是 HTTP header ;而mozilla 系列的瀏覽器剛剛好相反。」是不對的。在網上搜索「meta的作用」,很容易就可以找到一堆meta標籤的說明。比如《HTML中meta的作用》裡 提到:「meta是用來在HTML文檔中模擬HTTP協議的響應頭報文。」在meta標籤中寫和在HTTP頭裡寫是一樣的,這也是為瞭解決用普通HTML 寫網頁的人無法自行定義HTTP頭的問題。但是,meta是一個html標籤,所以必須進入到html解析的步驟才能生效,而生效後,瀏覽器會退回幾步, 重新設置好HTTP頭從頭再開始解碼、解析html。所以meta中寫的內容會覆蓋HTTP頭裡的內容,無論哪個瀏覽器都是這樣的。如果像他所說的, mozilla系列瀏覽器是HTTP Header優先於HTML內的標識,那meta標籤不就等於沒有用了嗎?這不符合html標準。

《一個 utf-8 網頁在 IE6 下的BUG》中 所說的出現空白頁必須的3項條件(1.title標籤裡的內容為中文其他雙字節字符;2.指定網頁編碼的 meta 信息在 title 標籤的下方;3.另存或轉換utf-8編碼時沒有包括 unicode 簽名 (BOM))基本上正確,不過不夠全面,具體的原因我上面已經詳細分析過了。還有BOM不能算簽名吧,唉,就叫他BOM好了,Byte Order Mark,字節序標識,用於UTF-16編碼的文件,在UTF-8編碼的文件中不需要標識字節序,所以被用來標識這是一個UTF-8編碼文件。

這個問題還是IE的兼容性問題,在解碼的時候如果遇到錯誤的編碼就中斷解碼。畢竟html代碼是從網絡上傳輸過來的,很可能傳錯幾個字節,所以解碼 程序不必弄的這麼嚴格吧。亂碼頁面起碼讓瀏覽者知道網頁已經打開,有點經驗的會知道切換網頁編碼,如果是空白頁很可能被瀏覽者認為是網頁本身就是空白的, 從而放棄瀏覽。IE在html解析和CSS解析上容錯性很好的呀,怎麼解碼這塊會做的這麼嚴格呢。某些天天喊叫IE兼容性好,IE能正常瀏覽的網頁多的 人,你看這個,這個,嘿嘿。

測試了一下,blogspot,sitesled,Blogger Spaces上的頁面都是返回的Content-Type: text/html,所以他們都會出現空白頁的問題。好像Apache默認情況下不會亂聲明編碼方式的,無論是國外的主機還是國內的主機。我的Blog的頁面的HTTP Header都包含Content-Type: text/html; charset=UTF-8, 後台的頁面也都有,一般不會有問題。比較奇怪的是,我查了WordPress和K2所有的文件,也沒發現哪兒用Header命令設置過這個HTTP頭。一 些輸出RSS和atom或者是發送trackback的文件都很仔細的用Header命令設置這個HTTP頭,不過不存在一個地方用了Header命令從 而一勞永逸地使所有輸出的頁面都包含這個頭的。難道Apache在處理PHP文件的時候會自動檢測文件的編碼方式並設置HTTP頭嗎?WordPress 自帶的WordPress Default和WordPress Classic模版都把單獨放在title標籤的前面,可能WordPress開發組注意到了這個問題,K2模版則沒有注意這個問題。blogger.com的模版也沒有注意這個問題,難道老外都習慣了meta一起放在title下面?

另外,這個是IE的bug,不過也不要認為你用的是MyIE、MyIE2、遨遊Maxthon、GreenBrowser、騰訊TT就不會受到影響,他們都是以IE為核心的,IE的bug他們一個個都跑不掉。儘早投降吧,投到Firefox或者Opera的懷抱裡來吧。

4 thoughts on “IE中打開UTF-8編碼title為中文的網頁會顯示空白頁的問題

  1. 你的文章很有幫助,可是沒幫到我 🙁 。可否向你打聽一下我遇到的問題,何處有解?
    下面這個是我的網頁。
    http://ant4js.blogspot.com/2009/01/navigator1.html
    在 IE6 中,如果我多按幾次 “一頁", “二頁", “三頁",最後中文就會變成亂碼;最
    奇怪的是 google 廣告部分的中文卻是正常的。然後按"重新載入",又可以看到中
    文了。我加了 meta no-cache 也沒用。拿掉 TITLE 也沒用。
    如果您知道何處可以找到解法,懇請不吝告知,不勝感激。

  2. 後來我將原有的 TITLE 拿掉,改成英文的內容,就沒有中文亂碼的情形了。
    你的文章還是有幫到我 🙂 。但是上述方法必須犧牲原有較好的 TITLE。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *