UTF-8的BOM问题

2008-5-27  18:04

     首先要说的是这个问题太气人了,害我搞了一天才发现是这个问题,大坏蛋!而且又这么隐秘,用他们的话说就是:一般人不知道。

     在集成UCenter的时候,连接测试老是不成功,但是我写的日志又证明是对的,简直是郁闷至极,于是认为肯定是UCenter的问题,所以开始研究它的结构,看函数,这里花了半天时间………

     好不容易找到了它的测试函数,这个函数叫onping(),使用get_url_code()构造出client端的url及参数后再调用test_api()进行测试。找到这个函数后当然就比较容易了。server和client连接测试成功的话一般是返回一个string类型的1,但是有一个诡异的问题就是我查看返回的这个值,明明是个1,但是在用var_dump输出的时候却显示的是string(4)。我郁闷了,这也太诡异了吧,我猜测是不是哪里有输出空白什么的,因为如果是乱码的话打印在浏览器上有时候不会显示出来,但是strlen()函数则是可以得到它真正的长度的。

    于是我加了个断点:
if($action == ‘test’) {
  exit;
  exit(API_RETURN_SUCCEED);
}
按理说它是什么都不会输出的了,可惜它照样有输出,是string(3),那就证明是在前面有输出了,然后我把exit;前移,一直移到最开始,就像这样:
<?php
exit;
居然还是输出string(3)!

    这下傻眼了,还会有什么问题呢?会不会是编码问题呢?编码不对的话会导致N多问题,类似白屏,乱码什么的,我看了下文件编码,是UTF-8啊,对了,突然想到一次看到Notepad++上有个UTF-8的BOM选项,会不会是这个呢?于是把现在有的bom去掉。

   再看结果:成功!我的眼睛顿时湿润了,等了好久终于等到今天,梦了好久终于把梦实现…….

   我估计原因是编辑器自动加上了BOM选项,而PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。由于必须在<?或者<? php后面的代码才会作为PHP代码执行,所以这三个字符将会直接输出。而我写到日志文件的东西,因为BOM会隐藏,所以想当然的认为自己的是正确的,汗……

   附BOM相关资料:

     UTF-8 BOM——Byte Order Mark又叫UTF-8 签名,其实UTF-8 的BOM对UFT-8没有作用,是为了支援UTF-16,UTF-32才加上的BOM,它是Unicode规范中推荐的标记字节顺序的方法。UTF-8以字节为编码单元,没有字节序的问题,而UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是 “奎”还是“乙”?

      在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。

      这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。

      UTF-8虽然不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。

     所以简单的说BOM签名的意思就是告诉编辑器当前文件采用何种编码,方便编辑器识别,但是BOM虽然在编辑器中不显示,但是会产生输出,就像多了一个空行。如果文件保存时,选择了使用 BOM,那么就可能会出现 headers already sent 的问题。因为 Web 服务器软件可能不认识 BOM,所以就把 BOM 的两个特殊字节当做字符发送给浏览器了。这时再调用 session_start() 等函数,就会出现 headers already sent 的问题。

发表评论