神奇的无BOM UTF-8文本

先来玩个小把戏(trick)

把这个压缩包test.zip下回来,里面有两个文件,一个是test.txt,另外一个是test.txt.bak,两个文件是一样的,bak只是个备份

用记事本打开test.txt,可以看到里面的内容是这样的:

把“瓧”前面的空格删掉,注意只删一个,表删错了

按下Ctrl+s保存文本,再重新打开,发现变成这样子了

为什么会这么离奇?

再玩多一个小把戏。打开记事本,输入下面两个字,“涓狽”,注意“狽”是繁体

保存,文件名随意。关闭程序重新打开,发现变成了这样

太离奇了,这难道是Windows的bug?其实不是。先补充一句,以上两个把戏仅在Windows简体中文系统有效。这把戏要多少有多少,繁体系统按道理也可以拼凑出来。

这种神奇现象的原因就在于Windows系统对无BOM文本的编码检测。文本文件在ASCII码时代是没有头(header)的,即使到了各多字节编码混战的时代,仍然没有头。其他文件格式,比如bmp,都是有头的,所以Linux系统不需扩展名从header就可以识别文件格式。进入了Unicode时代,文本文件终于有了BOM标记,BOM算得上是文本文件的头。众所周知,Unicode(UTF-16)以小尾(little-endian)顺序存放的BOM是0xFF、0xFE,以大尾(big-endian)顺序存放的BOM是0xFE、0xFF,这两个字节要先于文本写入。UTF-8文本不存在大小尾问题,但微软为UTF-8文本文件默认加上了0xEF、0xBB、0xBF的BOM。

倘若使用UltraEdit将UTF-8文本的BOM剔除,保存后的文件用记事本打开仍然是正常的。也就是说,Windows支持无BOM的UTF-8文本。上面的两个小把戏中的文本的离奇表现都是因为被识别为无BOM的UTF-8文本。Windows对文本编码识别的优先度是:依据BOM>依据UTF-8字符串检测算法>ANSI。

“涓狽”的GBK编码是0xE4B8 0xAA4E,保存为文件再打开时,E4B8AA是“个”的UTF-8编码,4E是“N”的ASCII码,整个字符串通过了检测函数,自然被认为是UTF-8字符串而不是GBK字符串了。

第一个小把戏中,删除空格前整个字符串没有通过检测函数,因此被当成GBK编码;删除空格后,通过了检测函数,因此被当成UTF-8编码。

那到底你想要的是GBK编码呢还是UTF-8编码呢?如果你想要GBK编码方式,“涓狽”这种情况,很遗憾,无论你怎样保存,系统还是会当成UTF-8。虽然这种概率很低,但并不是不存在。当然你将“涓狽”以UTF-8+BOM方式保存,就绝对不会出错了。

选择有BOM的UTF-8还是无BOM的UTF-8,要看场合。Windows默认有BOM,因此编写程序一定要支持BOM,检查前3个字节就行,非常简单。编写的程序保存UTF-8文本,也应当加上BOM,这不是多余,在一些情况下,UTF-8是能被当作多字节编码理解的。php的include文件,如果是UTF-8编码,不能加BOM。余的wordpress后台一直有问题,登录也不正常,原因在于修改某插件的一些代码加入中文字符后保存为UTF-8编码多了BOM。删掉php文件的BOM后就正常了。

2010年6月3日 | 归档于 技术
本文目前尚无任何评论.

发表评论

XHTML: 您可以使用这些标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: