很久很久以前(大概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___FCKpd___0gt;
<title><$BlogPageTitle___FCKpd___0gt;</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 | ||||
GBK | 正常 | 正常 | 73 | t11.html |
UTF-8(no BOM) | 空白页 | 使用GBK解码形成乱码 | 80 | t12.html |
UTF-8(BOM) | 正常 | 正常 | 83 | t13.html |
meta在前 | ||||
GBK | 使用UTF-8解码形成乱码 | 使用UTF-8解码形成乱码 | 144 | t21.html |
UTF-8(no BOM) | 正常 | 正常 | 151 | t22.html |
UTF-8(BOM) | 正常 | 正常 | 154 | t23.html |
meta在后 | ||||
GBK | 使用UTF-8解码形成乱码 | 使用UTF-8解码形成乱码 | 144 | t31.html |
UTF-8(no BOM) | 空白页 | 正常 | 151 | t32.html |
UTF-8(BOM) | 正常 | 正常 | 154 | t33.html |
---加了Header语句后--- | ||||
无meta | ||||
GBK | 使用UTF-8解码形成乱码 | 使用UTF-8解码形成乱码 | 133 | t11.php |
UTF-8(no BOM) | 正常 | 正常 | 140 | t12.php |
UTF-8(BOM) | 正常 | 正常 | 143 | t13.php |
meta在前 | ||||
GBK | 使用UTF-8解码形成乱码 | 使用UTF-8解码形成乱码 | 204 | t21.php |
UTF-8(no BOM) | 正常 | 正常 | 211 | t22.php |
UTF-8(BOM) | 正常 | 正常 | 214 | t23.php |
meta在后 | ||||
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的怀抱里来吧。