探索UTF8中文编码的BOM标记问题

探索UTF-8中文编码的BOM标记问题 - Keengle's Blog

探索UTF-8中文编码的BOM标记问题

七月 22nd, 2009
发表评论
阅读评论

一直都被中文编码的问题困扰,因为在客户端存了中文的cookie,在服务器里问题编码出问题,今天又研究了一下这个问题。

我把一个含有中文的文本保存为utf-8格式的文件,文本的内容为:

abcdefgKeengle2009中文

 

之后用python来读取这里面的内容如下:

>>> f=open('1.txt','r')

>>> s=f.read()

>>> f.close()

>>> s

'\xef\xbb\xbfabcdefgKeengle2009\xe4\xb8\xad\xe6\x96\x87'

>>> print s.decode('utf-8').encode('gbk')

Traceback (most recent call last):

  File "", line 1, in 

UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0:

illegal multibyte sequence

 

竟然发现在字符串开头多了三个字节(也就是\xef\xbb\xbf),就是这三个字节转为unicode后无法用

 

gbk编码。奇怪,怎么会在文件的开头多了三个字节呢,又开始了我的Google之旅。

找到了两篇文章,很有助于我的理解。

 

第一篇是详细介绍各种编码的:

5UTF的字节序和BOM

UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如Unicode编码是594EUnicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是” 还是

 

Unicode规范中推荐的标记字节顺序的方法是BOMBOM不是“Bill Of Material”BOM表,而是Byte Order MarkBOM是一个有点小聪明的想法:

UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFEUCS中是不存在的字符,所以不应该出现在实际传输中。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来标记文本文件的编码方式的。

详见:http://www.blogjava.net/dreamstone/archive/2006/11/28/83936.html  

 

找到的另一篇文章的作者也被BOM所困扰,其详细解释了出现问题的情形,但解决问题的办法似乎也是治标的了。

PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。在Bo-Blogwiki看 到,同样使用PHPBo-Blog也一样受到BOM的困扰。其中有提到另一个麻烦:COOKIE送出机制的限制,在这些文件开头已经有BOM的文件 中,COOKIE无法送出(因为在COOKIE送出前PHP已经送出了文件头),所以登入和登出功能失效。一切依赖COOKIESESSION实现的功 能全部无效。这个应该就是Wordpress后台出现空白页面的原因了,因为任何一个被执行的文件包含了BOM,这三个字符都将被送出,导致依赖 cookiessession的功能失效。
解决的办法嘛,如果只包含英文字符(或者说ASCII编码内的字符),就把文件存成ASCII码方式吧。用UE等编辑器的话,点文件->转换 ->UTF-8ASCII,或者在另存为里选择ASCII编码。如果是DOS格式的行尾符,可以用记事本打开,点另存为,选ASCII编码。如果 包含中文字符的话,可以用UE的另存为功能,选择“UTF-8 BOM”即可。

详见:http://blog.asupe.com/sam/?p=211 

 

在我前面的试验中,要想成功的输出内容,就必须跳过前面的三个字节,或者跳第1unicode字符:


>>> us=s.decode('utf-8')

>>> print us[1:].encode('gbk')

abcdefgKeengle2009中文

 

当然,也可以在notepad++里把文件的编码改为“UTF-8BOM编码格式”,就不用作这个处理了。

但我们读取文件的时候都不知道这个utf-8文件是否有BOM标记,或者在读取客户端发来的CookieUTF-8的值是否有BOM标记,要怎么样处理才能得到优雅的代码呢?也许这会成为困扰我的下一个问题。

原文地址:https://www.cnblogs.com/lexus/p/2391670.html