先来说说什么是BOM,BOM全称是“Byte Order Mark”,中文名译作“字节顺序标记”。
下面是百科中对BOM的具体说明:在UCS编码中有一个叫做“Zero Width No-Break Space”,中文译名作“零宽无间断间隔”的字符,它的编码是“FE FF”。而“FF FE”在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符“Zero Width No-Break Space”。这样如果接收者收到“FE FF”,就表明这个字节流是Big-Endian的;如果收到“FF FE”,就表明这个字节流是Little- Endian的。因此字符“Zero Width No-Break Space”(“零宽无间断间隔”)又被称作BOM。字符U+FEFF如果出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。如果它出现在字节流的中间,则表达零宽度非换行空格的意义,用户看起来就是一个空格。从Unicode 3.2开始,U+FEFF只能出现在字节流的开头,只能用于标识字节序,就如它的名称——字节序标记——所表示的一样;除此以外的用法已被舍弃。取而代之的是,使用U+2060来表达零宽度无断空白。
不同编码的字节顺序标记并不相同,比如UTF-8的BOM是“EF BB BF”,UTF-16(大端序)是“FE FF”,UTF-16(小端序)是“FF FE”。UTF-8不需要 BOM 来表明字节顺序,但可以用BOM来表明编码方式。字符 "Zero Width No-Break Space" 的 UTF-8 编码是 EF BB BF。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。
前面提到Windows是利用BOM来识别UTF-8编码的文本文件的,利用它自带的记事本保存UTF-8编码的文件时,会在文件开头写入“EF BB BF”,通常来说它并不会带来什么麻烦,一般用户也感觉不到它们的存在。但是对于PHP来说,这简直就是灾难!
曾经在调试一个基于PHP的Web系统时,由于一时偷懒,用记事本对其安装时使用的SQL文件(UTF-8编码)进行了修改,然后保存。接着进行安装,这时,问题就来了,在执行到数据库创建部分的时候总是弹出错误信息“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"' at line 1”,第一行就报错了,以为是语法有错误,于是便打开文件仔细检查,检查完毕发现并没有错!反复折腾了一个下午还是不行,最后不得已拿出了WinHex跟未修改过的文件进行比对,发现出错文件的开头多了“EF BB BF”这几个字节,抱着试一试的心态将其去掉后保存,重新安装居然成功了!有了这次经历再也不敢随便用记事本对PHP文件进行编辑了。
网上有人提到另一个麻烦:“受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。
由于BOM通常状态的“不可见性”,在出现错误时往往反复调试也未能发现是它造成的,因此为了避免这样的麻烦,在进行类似开发时尽量使用无BOM的UTF-8存储文件,对于已经写入BOM的文件,可以用十六进制编辑器手动去除。
下面是百科中对BOM的具体说明:在UCS编码中有一个叫做“Zero Width No-Break Space”,中文译名作“零宽无间断间隔”的字符,它的编码是“FE FF”。而“FF FE”在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符“Zero Width No-Break Space”。这样如果接收者收到“FE FF”,就表明这个字节流是Big-Endian的;如果收到“FF FE”,就表明这个字节流是Little- Endian的。因此字符“Zero Width No-Break Space”(“零宽无间断间隔”)又被称作BOM。字符U+FEFF如果出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。如果它出现在字节流的中间,则表达零宽度非换行空格的意义,用户看起来就是一个空格。从Unicode 3.2开始,U+FEFF只能出现在字节流的开头,只能用于标识字节序,就如它的名称——字节序标记——所表示的一样;除此以外的用法已被舍弃。取而代之的是,使用U+2060来表达零宽度无断空白。
不同编码的字节顺序标记并不相同,比如UTF-8的BOM是“EF BB BF”,UTF-16(大端序)是“FE FF”,UTF-16(小端序)是“FF FE”。UTF-8不需要 BOM 来表明字节顺序,但可以用BOM来表明编码方式。字符 "Zero Width No-Break Space" 的 UTF-8 编码是 EF BB BF。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。
前面提到Windows是利用BOM来识别UTF-8编码的文本文件的,利用它自带的记事本保存UTF-8编码的文件时,会在文件开头写入“EF BB BF”,通常来说它并不会带来什么麻烦,一般用户也感觉不到它们的存在。但是对于PHP来说,这简直就是灾难!
曾经在调试一个基于PHP的Web系统时,由于一时偷懒,用记事本对其安装时使用的SQL文件(UTF-8编码)进行了修改,然后保存。接着进行安装,这时,问题就来了,在执行到数据库创建部分的时候总是弹出错误信息“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"' at line 1”,第一行就报错了,以为是语法有错误,于是便打开文件仔细检查,检查完毕发现并没有错!反复折腾了一个下午还是不行,最后不得已拿出了WinHex跟未修改过的文件进行比对,发现出错文件的开头多了“EF BB BF”这几个字节,抱着试一试的心态将其去掉后保存,重新安装居然成功了!有了这次经历再也不敢随便用记事本对PHP文件进行编辑了。
网上有人提到另一个麻烦:“受COOKIE送出机制的限制,在这些文件开头已经有BOM的文件中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIE、SESSION实现的功能全部无效。
由于BOM通常状态的“不可见性”,在出现错误时往往反复调试也未能发现是它造成的,因此为了避免这样的麻烦,在进行类似开发时尽量使用无BOM的UTF-8存储文件,对于已经写入BOM的文件,可以用十六进制编辑器手动去除。