近日在调测一个UTF8编码的中文Zen Cart网站时遇到一件怪事,网页显示文字正常,用ie的察看源文件(记事本打开)却发现乱码,firefox没有这个问题。经在网上多方查证和多次测试,解决了这个问题,其实是UTF-8文件的Unicode签名BOM(Byte Order Mark)问题。
BOM(Byte Order Mark),是UTF编码方案里用于标识编码的标准标记,在UTF-16里本来是FF FE,变成UTF-8就成了EF BB BF。这个标记是可选的,因为UTF8字节没有顺序,所以它可以被用来检测一个字节流是否是UTF-8编码的。微软做这种检测,但有些软件不做这种检测,而把它当作正常字符处理。
微软在自己的UTF-8格式的文本文件之前加上了EF BB BF三个字节, windows上面的notepad等程序就是根据这三个字节来确定一个文本文件是ASCII的还是UTF-8的, 然而这个只是微软暗自作的标记, 其它平台上并没有对UTF-8文本文件做个这样的标记。
也就是说一个UTF-8文件可能有BOM,也可能没有BOM,那么怎么区分呢?三种方法。1,用UltraEdit-32打开文件,切换到十六进制编辑模式,察看文件头部是否有EF BB BF。2,用Dreamweaver打开,察看页面属性,看“包括Unicode签名BOM”前面是否有个勾。3,用Windows的记事本打开,选择 “另存为”,看文件的默认编码是UTF-8还是ANSI,如果是ANSI则不带BOM。
我找到Zen Cart的模版文件中的html_header.php,发现文件果然不带BOM,用UltraEdit-32另存为的方式加上BOM后,再上传html_header.php,一切正常。
注意用Convertz把gb2312文件转换成UTF-8文件时,默认设置是不带BOM的。不带BOM可能出现上述乱码问题,但是带 BOM,对于php的include文件要小心,会在php字节流前面多出EF BB BF,提前输出到显示器有可能会带来程序错误。一个解决方案是凡是被include的文件都保存为ANSI,主文件可以是UTF-8。要想把一个文件去掉 BOM,使用UlterEdit打开, 切换到十六进制编辑模式,把最前面三个字节(就是那该死的 EF BB BF)替换为20,保存(注意关闭保存时自动备份的功能),再切换到默认编辑模式,把最前面的三个空格去掉就可以了。
另外还学到一些编码的小知识:所谓的unicode保存的文件实际上是utf-16,只不过恰好跟unicode的码相同而已,但在概念上unicode与utf是两回事,unicode是内存编码表示方案,而utf是如何保存和传输unicode的方案。utf-16还分高位在前 (LE)和高位在后(BE)两种。官方的utf编码还有utf-32,也分LE和BE。非unicode官方的utf编码还有utf-7,主要用于邮件传输。utf-8的单字节部分是和iso-8859-1兼容的,这主要是一些旧的系统和库函数不能正确处理utf-16而被迫出来的,而且对英语字符来说,也节省保存的文件空间(以非英语字符浪费空间为代价)。在iso-8859-1的时候,utf8和iso-8859-1都是用一个字节表示的,当表示其它字符的时候,utf-8会使用两个或三个字节。
?
工作需要必须创建UTF-16格式的文件.这几天在网络上找,似乎没有此类话题,无论是国外还是国内.
代码:
1 2 3 4 5 6 7 |
OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(new File("c:\2.csv")), "UTF-16"); fos.write("你好"); fos.flush(); fos.close(); |
生成文件之后,我在windows平台打开.使用editeplus是乱码.使用记事本打开不乱码,保存时察看文件编码确是unicode big endian. 使用写字板打开时乱码. 使用excel打开仍然乱码.
我不过,使用下面代码,就可以顺利创建UTF-8的文件.
1 2 3 4 5 6 7 |
OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(new File("c:\2.csv")), "UTF-8"); fos.write("你好"); fos.flush(); fos.close(); |
哪位碰到过这个问题.帮忙看看
答:一个简单的问题,楼主怎么弄得这么复杂?楼主的问题是:用UTF-16格式保存汉字,要求在WINDOW平台使用:写字板、EXCEL、WORD能正常打开且不乱码(尤其是用EXCEL能打开,用于导入.csv文件)。很简单啊,用我的代码,保证你100%成功。
1 2 3 4 5 6 7 8 9 |
OutputStreamWriter fos = new OutputStreamWriter(new FileOutputStream(new File("c:\2.csv")), "UTF-16LE"); fos.write(0xFEFF); fos.write("你好"); fos.flush(); fos.close(); |
A:
提示楼主,写一个编码类型的几个字节到文件里,这样虽然能在Windows下面的某些程序看到,但你的程序下次处理这个程序时,一定要注意这个问题。
前几个字节是有特殊意义的。
下面是部分BOM的介绍
在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来标记文本文件的编码方式的。
个人并不建议这样做,因为后患无穷。