12

まず、いくつかの背景: Python を使用して Web アプリケーションを開発しています。すべての (テキスト) ファイルは現在、BOM 付きの UTF-8 で保存されています。これには、すべての HTML テンプレートと CSS ファイルが含まれます。これらのリソースは、DB にバイナリ データ (BOM とすべて) として保存されます。

DB からテンプレートを取得するときは、 を使用してそれらをデコードしますtemplate.decode('utf-8')。HTML がブラウザーに到着すると、HTTP 応答本文の先頭に BOM が表示されます。これにより、Chrome で非常に興味深いエラーが生成されます。

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

Chrome<html>は、BOM を見てコンテンツと間違えると、タグを自動的に生成するようで、実際の<html>タグをエラーにします。

では、Python を使用して、UTF-8 でエンコードされたテンプレートから BOM を削除する最良の方法は何ですか (存在する場合、将来これを保証することはできません)。

CSS のような他のテキストベースのファイルの場合、主要なブラウザーは BOM を正しく解釈 (または無視) しますか? それらは、.xml のないプレーン バイナリ データとして送信されます.decode('utf-8')

注: Python 2.5 を使用しています。

ありがとう!

4

4 に答える 4

24

あなたが述べているので:

私の(テキスト)ファイルはすべて、現在BOMを使用してUTF-8に保存されています

次に、「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が存在しない場合も正しく機能します。

于 2010-03-17T03:47:42.417 に答える
10

デコード後に最初の文字をチェックして、BOM かどうかを確認します。

if u.startswith(u'\ufeff'):
  u = u[1:]
于 2010-03-16T17:33:19.120 に答える
1

以前に受け入れられた答えは間違っています。

u'\ufffe'キャラクターではありません。あなたがそれをユニコード文字列で得るならば、誰かが力強く詰め込んだ。

BOM(別名ZERO WIDTH NO-BREAK SPACE)はu'\ufeff'

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

これ(Ctrl-FでBOMを検索)とこれこれ(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の場合は2つの異なるBOMをチェックし UTF-32の場合は少なくとも2つの異なるBOMをチェックする必要があります。それぞれの方法が1つしかない場合は、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バイトである必要があります。入力のBOM後の部分をデコードするために使用できるエンコーディングと、BOMの長さ(存在する場合)を返します。

妄想的な場合は、別の2つの(非標準の)UTF-32順序を許可できますが、Pythonはそれらのエンコードを提供せず、実際の発生について聞いたことがないので、気にしません。

于 2010-03-16T22:50:32.640 に答える
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()
于 2010-03-16T17:11:26.533 に答える