带BOM的UTF-8html和CSS文件(以及如何用Python删除BOM)

2024-06-06 22:19:48 发布

您现在位置:Python中文网/ 问答频道 /正文

首先,一些背景知识:我正在使用Python开发一个web应用程序。我的所有(文本)文件当前都与BOM一起存储在UTF-8中。这包括我所有的HTML模板和CSS文件。这些资源作为二进制数据(BOM和all)存储在我的数据库中。

当我从数据库中检索模板时,我使用template.decode('utf-8')对它们进行解码。当HTML到达浏览器时,BOM出现在HTTP响应体的开头。这在Chrome中产生了一个非常有趣的错误:

Extra <html> encountered. Migrating attributes back to the original <html> element and ignoring the tag.

当Chrome看到BOM并将其误认为是内容时,它似乎会自动生成一个<html>标记,从而使真正的<html>标记出错。

那么,使用Python,从UTF-8编码的模板中删除BOM的最佳方法是什么(如果它存在——我不能保证将来会这样做)?

对于其他基于文本的文件,如CSS,主要浏览器会正确解释(或忽略)BOM吗?它们将作为纯二进制数据发送,而不使用.decode('utf-8')

注意:我使用的是Python2.5。

谢谢!


Tags: 文件数据文本模板数据库html二进制浏览器
3条回答

解码后检查第一个字符是否是BOM:

if u.startswith(u'\ufeff'):
  u = u[1:]

先前接受的答案是错误的。

u'\ufffe'不是字符。如果你用unicode字符串,就会有人把它塞得满满的。

BOM(也称为零宽度不间断空格)是u'\ufeff'

>>> UNICODE_BOM = u'\N{ZERO WIDTH NO-BREAK SPACE}'
>>> UNICODE_BOM
u'\ufeff'
>>>

读取this(Ctrl-F搜索BOM)和thisthis(Ctrl-F搜索BOM)。

这里有一个正确的和防错/防脑损伤的答案:

将输入解码成unicode_str。然后执行以下操作:

# If I mistype the following, it's very likely to cause a SyntaxError.
UNICODE_BOM = u'\N{ZERO WIDTH NO-BREAK SPACE}'
if unicode_str and unicode_str[0] == UNICODE_BOM:
    unicode_str = unicode_str[1:]

另外:使用命名常量比使用一组看似武断的六边形文字更能让读者了解正在发生的事情。

更新不幸的是,标准Python库中似乎没有合适的命名常量。

唉,编解码器模块只提供了“陷阱和错觉”:

>>> import pprint, codecs
>>> pprint.pprint([(k, getattr(codecs, k)) for k in dir(codecs) if k.startswith('BOM')])
[('BOM', '\xff\xfe'),   #### aarrgghh!! ####
 ('BOM32_BE', '\xfe\xff'),
 ('BOM32_LE', '\xff\xfe'),
 ('BOM64_BE', '\x00\x00\xfe\xff'),
 ('BOM64_LE', '\xff\xfe\x00\x00'),
 ('BOM_BE', '\xfe\xff'),
 ('BOM_LE', '\xff\xfe'),
 ('BOM_UTF16', '\xff\xfe'),
 ('BOM_UTF16_BE', '\xfe\xff'),
 ('BOM_UTF16_LE', '\xff\xfe'),
 ('BOM_UTF32', '\xff\xfe\x00\x00'),
 ('BOM_UTF32_BE', '\x00\x00\xfe\xff'),
 ('BOM_UTF32_LE', '\xff\xfe\x00\x00'),
 ('BOM_UTF8', '\xef\xbb\xbf')]
>>>

更新2如果尚未对输入进行解码,并且希望检查其是否为BOM,则需要检查UTF-16的两个不同BOM,以及UTF-32的至少两个不同BOM。如果只有一种方法,那么你就不需要BOM了,是吗?

这里,我的解决方案是从我自己的代码中逐字输入:

def check_for_bom(s):
    bom_info = (
        ('\xFF\xFE\x00\x00', 4, 'UTF-32LE'),
        ('\x00\x00\xFE\xFF', 4, 'UTF-32BE'),
        ('\xEF\xBB\xBF',     3, 'UTF-8'),
        ('\xFF\xFE',         2, 'UTF-16LE'),
        ('\xFE\xFF',         2, 'UTF-16BE'),
        )
    for sig, siglen, enc in bom_info:
        if s.startswith(sig):
            return enc, siglen
    return None, 0

输入s应该至少是输入的前4个字节。它返回可用于解码输入的post-BOM部分的编码,加上BOM的长度(如果有的话)。

如果你多疑,你可以允许另外2个(非标准的)UTF-32订单,但是Python没有为它们提供编码,而且我也从来没有听说过实际发生的事情,所以我不介意。

既然你说:

All of my (text) files are currently stored in UTF-8 with the BOM

然后使用“utf-8-sig”编解码器对它们进行解码:

>>> s = u'Hello, world!'.encode('utf-8-sig')
>>> s
'\xef\xbb\xbfHello, world!'
>>> s.decode('utf-8-sig')
u'Hello, world!'

它会自动删除预期的BOM,如果BOM不存在,则可以正常工作。

相关问题 更多 >