2010-03-16 32 views
12

首先,一些背景:我正在開發一個使用Python的Web應用程序。我所有的(文本)文件都以BOM形式存儲在UTF-8中。這包括我所有的HTML模板和CSS文件。這些資源作爲二進制數據(BOM和全部)存儲在我的數據庫中。帶BOM的UTF-8 HTML和CSS文件(以及如何用Python刪除BOM)

當我從DB中檢索模板時,我使用template.decode('utf-8')對它們進行解碼。當HTML到達瀏覽器時,BOM將出現在HTTP響應主體的開頭。這在Chrome就會產生一個非常有趣的錯誤:

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

鉻似乎當它看到的BOM和錯誤,它的內容,使得真正的<html>標籤錯誤時自動生成<html>標籤。

因此,使用Python,從我的UTF-8編碼模板中刪除BOM的最佳方式是什麼(如果它存在 - 我不能保證在未來)?

對於其他基於文本的文件,如CSS,主要瀏覽器是否會正確解釋(或忽略)BOM?它們被作爲簡單的二進制數據發送,沒有.decode('utf-8')

注意:我正在使用Python 2.5。

謝謝!

回答

23

既然你的狀態:

我的(文本)文件的所有當前存儲 在UTF-8與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!' 

它會自動刪除預期的物料清單,並在物料清單不存在的情況下正常工作。

+0

哦!非常好!我會盡快嘗試。 – Cameron 2010-03-17 12:03:39

+0

精美地工作(儘管Chrome神祕地停止了提供錯誤,即使使用舊的(錯誤的)代碼也是如此 - 這就是我一次完成一大堆更改的結果)。 – Cameron 2010-03-18 00:34:10

0

您可以使用類似的東西刪除BOM:

import os, codecs 
def remove_bom_from_file(filename, newfilename): 
    if os.path.isfile(filename): 
     # open file 
     f = open(filename,'rb') 

     # read first 4 bytes 
     header = f.read(4) 

     # check if we have BOM... 
     bom_len = 0 
     encodings = [ (codecs.BOM_UTF32, 4), 
      (codecs.BOM_UTF16, 2), 
      (codecs.BOM_UTF8, 3) ] 

     # ... and remove appropriate number of bytes  
     for h, l in encodings: 
      if header.startswith(h): 
       bom_len = l 
       break 
     f.seek(0) 
     f.read(bom_len) 

     # copy the rest of file 
     contents = f.read() 
     nf = open(newfilename) 
     nf.write(contents) 
     nf.close() 
+1

嗯,在閱讀前4個字節之後和測試BOM之前,您是否必須倒帶文件? 'f.seek(0)'。 – 2010-03-16 17:17:52

+0

@Konrad我錯過了,謝謝指出。這不是生產代碼:]。 – pajton 2010-03-16 17:25:01

+0

對我來說看起來不錯(使用「seek(0)」修復),但是當我試圖切割BOM時,我已經將整個文件存儲在內存中 - 內容的效率如何[2:](例如)在Python中?它是否創建整個字符串的副本? – Cameron 2010-03-16 17:29:26

10

解碼,看看它的BOM後檢查的第一個字符:

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

在非UTF-8文件的開頭是否會出現'u'\ ufffe''? 在我的情況(UTF-8)中BOM不會帶兩個「字符」(讀取:字節)嗎? – Cameron 2010-03-16 17:59:52

+0

'u'\ ufffe''可以在任何UTF或UCS編碼文件的開頭找到。 BOM是UTF-8中的三個字節,但它仍然是一個Unicode碼點。 – 2010-03-16 18:07:38

+0

好吧,爲了得到這個結果,我需要首先使用u = contents.decode('utf-8')來解碼文件的字節內容,然後才能使用你的方法因爲BOM是一個單一的代碼點。正確? – Cameron 2010-03-16 18:29:34

1

先前接受的答案是錯的。

u'\ufffe'不是一個字符。如果你用unicode字符串得到它,那麼有人已經塞進了很多。

的BOM(又名ZERO WIDTH NO-BREAK SPACE)是u'\ufeff'

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

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

這裏有一個正確和錯字/耐braino回答:

解碼你的投入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:] 

獎勵:使用命名常量讓讀者有更多的線索來是怎麼回事呢相比看似武斷hexoglyphics集合了一下。

更新不幸的是,標準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檢查TWO不同的物料清單,至少檢查UTF-32的不同物料清單,至少TWO。如果只有一種方法,那麼你不需要BOM,你會嗎?

這裏逐字從我自己的代碼unprettified是我解決這個:

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個字節的輸入。它返回可用於解碼輸入的BOM後部分的編碼,以及BOM(如果有)的長度。

如果你是偏執的人,你可以允許另外兩個(非標準的)UTF-32排序,但是Python不提供他們的編碼,我從來沒有聽說過實際發生,所以我不'麻煩了。

+0

我沒有看到「ZERO WIDTH NO-BREAK SPACE」在這裏使用,因爲它也是BOM(雙關語意圖),比U「\ uFEFF」更清晰。他們都需要了解關於BOM的先前知識。 – Cameron 2010-03-16 23:17:48

+0

@Cameron:可讀性來自任何你常用的名字,例如UNICODE_BOM。 – 2010-03-17 00:03:20

+0

@Cameron:我對BOM一無所知,但我知道什麼是「零寬度不間斷空間」,並且不知道u「\ uFEFF」是什麼。後者也很難確定我輸入的是正確的,因爲它的8個字符長度只包含3個字母數字字符,其中兩個字符彼此非常相似。 – 2010-03-17 00:06:56