原文:一次由注释引发的“血案”
有几天没写博客了,今天有点时间,正好把前几天遇到的一件“诡异”的问题记录一下。
我是在前几天学习CSS时遇到这个问题的。当时看到某个网站有一个CSS效果不错,就想研究一下。由于代码很短,我不想将整个页面保存下来,所以我点击"查看源文件",找到这段代码并拷贝到测试页面中。
它的主要部分是这样的:
/*file设为透明,并覆盖整个触发面*/ a.files input { margin-left:-350px; font-size:30px; cursor:pointer; filter:alpha(opacity=0); opacity:0; }
用FF查看效果,一切正常。但当我换到IE后,问题来了,没有出现预期的效果!
当然,大家看到这篇文章的标题可能就已经猜到大概原因是什么了。但当时于我却是一脸茫然(原网页在FF和IE下显示均正常)
于是我开始分析……
首先想到的是:
是不是我复制代码时遗漏了一些对FF来说不重要,但对IE却是必需的代码呢?
毕竟我只复制了一小段,而且CSS在各个浏览器中也确实有可能出现不兼容的情况。
为保险起见,我点击"查看源文件",并将其中的代码全部复制到测试页面中,运行,问题依旧。
这使我非常郁闷,为什么相同的代码,在别人那是正常的,在我这却有问题呢?
继续尝试。
这次我决定将他的整个网页保存下来。
于是我点击了IE的"另存为",将他的网页保存到本地,运行,效果出来了!
现在可以肯定是我前面的代码哪里错了。于是我再将保存在本地的网页打开,查看其中的代码,发现IE对上面的CSS代码进行了格式化:
A.files INPUT { FILTER: alpha(opacity=0); MARGIN-LEFT: -350px; FONT-SIZE: 30px; CURSOR: pointer; opacity: 0 }
大家就不用自己比对了,我直接说结果:这两段代码是完全一样的,除了大小写和属性定义的顺序之外。
我们知道CSS是不区分大小写的,而且通常来说,不相干的属性之间的顺序也是无关紧要的。但我还是抱着谨慎求证的态度决定亲自验证一下,我将之前的CSS用这个替换掉,然后用IE打开,然后……显示正常!
但谁能告诉我,这TM到底是为什么吗?
难道真的是大小写或者顺序的原因?不管你信不信,反正我是不信。
然后我决定再改回原来的代码再试一次。
a.files input { margin-left:-350px; font-size:30px; cursor:pointer; filter:alpha(opacity=0); opacity:0; }
用IE打开——他TM又没问题了!相同的代码,改回去再改回来,结果居然是不一样的?我当时就在想我是不是被上帝当成愤怒的小鸟给玩儿了?要不然怎么会发生这么反人类反科学的事情!
忽然,我发现最后这段代码与原来的代码只有一点不同,那就是注释。
难道……是……注释?
不知道为什么,可能是灵感突发,我忽然想到了编码这个问题。我在IE中查看了我的测试页面的编码:GB2312;我又查看了原网页的编码:UTF-8。
事实证明,果然是编码的问题。然而更多的问题来了:
1. 为什么编码会影响到效果?
2. 我查看了两个页面在FF下的编码,情况和IE是一样的(一个是UTF-8另一个是GB2312),为什么FF都可以正常显示呢?
3. 我后来是将整个页面通过"查看源文件"的方式复制下来的,为什么会和原网页的默认编码不一样呢?
经过一番思考和验证,我终于找到了第1和第3个问题的答案:
1.为什么编码会影响到效果
IE错误地解释了注释/*file设为透明,并覆盖整个触发面*/,因为这段字符是UTF-8编码的,最后的"面*/"三个字符的UTF-8编码为:0xE99DA2(面)、0x2A(*)、0x2F(/)
UTF-8将汉字用3个字节来编码,对英文字符是1个字节。GB2312对汉字是2字节,对英文字符和UTF-8一样是1字节
当IE试图用GB2312解释这段注释时,它错误地将0xA2(“面”字的最后一个字节)和0x2A(*)组合在一起,形成了GB2312字符:0xA22A,虽然最后的"/"正常解释,但"*"字符丢失导致了本该结束的注释没有结束。
我试着将注释内容换成别的中文字符,如"中中",发现在IE下也没有问题了,看来这是一个由不恰当的中文注释恰好导致的一个偶然的错误(很拗口吧,我故意的)
3. 为什么通过“查看源文件”扒下来的内容会和原网页的默认编码不一样
我仔细查看了使用“另存为”保存的页面源代码,其中有这么一句:
<META content="text/html; charset=utf-8" http-equiv=Content-Type>
原来人家设置了页面的默认编码为UTF-8的,但我的测试页面中却没有,所以IE使用了错误的编码来解析,导致出错。但诡异的是,我在"查看源文件"时无论是IE还是FF都没有这一句。这就是为什么我将“查看源文件”中的所有内容都复制过来也还是不行的原因。
至于第2点,我始终不太理解,为什么同样的情况IE不行但FF却是正常的?个人猜测是两个浏览器在实现上的差异导致的。如果你知道具体原因,希望能够不吝赐教。
------------------------------------
下面是总结时间:
1. 浏览器的"查看源文件"和真正的源文件是有差别的
2. 注释有时也会对程序的运行产生影响
在《Java解惑》一书中也提到过因为注释而产生问题的例子。书中的谜题15"令人晕头转向的Hello"和谜题16"行打印程序"两节,作者列举了两个因为注释而导致问题的例子: 谜题15的例子: /** * Generated by the IBM IDL-to-Java compiler, version 1.0 * from F:\TestRoot\apps\a1\units\include\PolicyHome.idl * Wednesday, June 17, 1998 6:44:40 o’clock AM GMT+00:00 */ 如果你没看过Java解惑这本书,估计很难一眼看出问题在哪,只是一段普通的注释嘛。 问题出在注释中的"\units"这里,"\u"在Java中用来对Unicode字符进行转义,后面必需紧跟着4位十六进制数字,即字符的Unicode编码。但这里,"\units"显然违反了此约定。 谜题16的例子: // Note: \u000A is Unicode representation of linefeed (LF) 这个例子的问题与我遇到的这个问题就更相似了。"\u000A"是一个Unicode换行符,因此在编译时,这行注释将被编译器理解为: // Note: is Unicode representation of linefeed (LF) 显然,这也是无法通过编译的。
3. 最后,一些看似可有可无的细节也有可能会导致莫名其妙的问题。因此在平时写程序时要注意总结和遵守规范。不要嫌这些规范死板和繁琐,它们都是前人根据多年的经验总结下来的,通常能够让你远离错误。
好了,今天就到此为止吧,一个看似简单的小问题,随便记录一下。