58

Pythonを使用して一連のソースコードファイルを読み取っていて、ユニコードBOMエラーが発生しています。これが私のコードです:

bytes = min(32, os.path.getsize(filename))
raw = open(filename, 'rb').read(bytes)
result = chardet.detect(raw)
encoding = result['encoding']

infile = open(filename, mode, encoding=encoding)
data = infile.read()
infile.close()

print(data)

ご覧のとおり、を使用してエンコードを検出しchardet、メモリ内のファイルを読み取って印刷しようとしています。BOMを含むUnicodeファイルでprintステートメントが失敗し、次のエラーが発生します。

UnicodeEncodeError:'charmap'コーデックは位置0-2の文字をエンコードできません:
文字は<undefined>にマップされます

デフォルトの文字セットを使用してBOMをデコードしようとしていて、失敗していると思います。これを防ぐために文字列からBOMを削除するにはどうすればよいですか?

4

7 に答える 7

86

BOMが存在するかどうかを確認する理由はなく、BOMが存在するかどうかを管理し、BOMが存在しない場合utf-8-sigとまったく同じように動作します。utf-8

# Standard UTF-8 without BOM
>>> b'hello'.decode('utf-8')
'hello'
>>> b'hello'.decode('utf-8-sig')
'hello'

# BOM encoded UTF-8
>>> b'\xef\xbb\xbfhello'.decode('utf-8')
'\ufeffhello'
>>> b'\xef\xbb\xbfhello'.decode('utf-8-sig')
'hello'

utf-8-sig上記の例では、BOMの存在に関係なく、指定された文字列を正しくデコードしていることがわかります。読んでいるファイルにBOM文字が存在する可能性が少しでもあると思われる場合は、それを使用するだけで、utf-8-sig心配する必要はありません。

于 2017-06-15T17:50:28.097 に答える
62

エンコードを明示的に使用しない限り、UTF-16をデコードするときにBOM文字を自動的に削除する必要がありますが、UTF-8は削除しないでくださいutf-8-sig。あなたはこのようなことを試すことができます:

import io
import chardet
import codecs

bytes = min(32, os.path.getsize(filename))
raw = open(filename, 'rb').read(bytes)

if raw.startswith(codecs.BOM_UTF8):
    encoding = 'utf-8-sig'
else:
    result = chardet.detect(raw)
    encoding = result['encoding']

infile = io.open(filename, mode, encoding=encoding)
data = infile.read()
infile.close()

print(data)
于 2012-11-27T19:16:19.647 に答える
28

Chewieの回答に基づいて、気の利いたBOMベースの検出器を作成しました。データが既知のローカルエンコーディングまたはBOMを使用したUnicode(テキストエディタが通常生成するもの)のいずれかである可能性がある一般的なユースケースでは、これで十分です。さらに重要なことに、とは異なりchardet、ランダムな推測を行わないため、予測可能な結果が得られます。

def detect_by_bom(path, default):
    with open(path, 'rb') as f:
        raw = f.read(4)    # will read less if the file is smaller
    # BOM_UTF32_LE's start is equal to BOM_UTF16_LE so need to try the former first
    for enc, boms in \
            ('utf-8-sig', (codecs.BOM_UTF8,)), \
            ('utf-32', (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)), \
            ('utf-16', (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)):
        if any(raw.startswith(bom) for bom in boms):
            return enc
    return default
于 2014-06-23T16:14:18.873 に答える
11

chardet2014年10月7日にリリースされた2.3.0バージョン以降、BOM_UTF8を自動的に検出します

#!/usr/bin/env python
import chardet # $ pip install chardet

# detect file encoding
with open(filename, 'rb') as file:
    raw = file.read(32) # at most 32 bytes are returned
    encoding = chardet.detect(raw)['encoding']

with open(filename, encoding=encoding) as file:
    text = file.read()
print(text)

注:テキストにBOMを残すエンコーディングをchardet返す場合が'UTF-XXLE'あります。、それを避けるために削除する必要があります-たとえば、 @ ivan_pozdeevの回答のように、この時点でBOMを自分で検出する方が簡単です。'UTF-XXBE''LE''BE'

UnicodeEncodeErrorUnicodeテキストをWindowsコンソールに印刷する際に回避するには、Python、Unicode、およびWindowsコンソールを参照してください。

于 2015-09-25T04:18:07.910 に答える
9

他の答えは非常に複雑だと思います。chardetバイナリファイルI/Oの下位レベルのイディオムにドロップダウンする必要がなく、Python標準ライブラリの一部ではない文字セットヒューリスティック()に依存せず、必要のない、より簡単な方法があります。 UTF-16ファミリにアナログがないように見える、めったに見られない代替エンコーディング署名(utf-8-sig対一般)。utf-8

私が見つけた最も簡単なアプローチは、UnicodeでBOM文字を処理し、コーデックに手間のかかる作業を行わせることです。Unicodeバイトオーダーマークは1つしかないため、データがUnicode文字に変換されると、そこにあるかどうかを判断したり、データを追加/削除したりするのは簡単です。BOMの可能性があるファイルを読み取るには:

BOM = '\ufeff'
with open(filepath, mode='r', encoding='utf-8') as f:
    text = f.read()
    if text.startswith(BOM):
        text = text[1:]

これは、すべての興味深いUTFコーデック(たとえば、、、、utf-8... )で機能しutf-16leutf-16be追加のモジュールを必要とせず、バイナリファイル処理または特定のcodec定数にドロップダウンする必要もありません。

BOMを作成するには:

text_with_BOM = text if text.startswith(BOM) else BOM + text
with open(filepath, mode='w', encoding='utf-16be') as f:
    f.write(text_with_BOM)

これはどのエンコーディングでも機能します。UTF-16ビッグエンディアンはほんの一例です。

ところで、これは却下することではありませんchardet。ファイルが使用するエンコーディングに関する情報がない場合に役立ちます。BOMの追加/削除には必要ありません。

于 2018-04-29T23:41:55.087 に答える
0

(ファイルではなく)文字列/例外に対する@ivan_pozdeevの回答の変形。Pythonの例外に詰め込まれたUnicodeHTMLコンテンツを扱っています(http://bugs.python.org/issue2517を参照) 。

def detect_encoding(bytes_str):
  for enc, boms in \
      ('utf-8-sig',(codecs.BOM_UTF8,)),\
      ('utf-16',(codecs.BOM_UTF16_LE,codecs.BOM_UTF16_BE)),\
      ('utf-32',(codecs.BOM_UTF32_LE,codecs.BOM_UTF32_BE)):
    if (any(bytes_str.startswith(bom) for bom in boms): return enc
  return 'utf-8' # default

def safe_exc_to_str(exc):
  try:
    return str(exc)
  except UnicodeEncodeError:
    return unicode(exc).encode(detect_encoding(exc.content))

あるいは、このはるかに単純なコードは、それほど大騒ぎせずに非ASCII文字を削除することができます。

def just_ascii(str):
  return unicode(str).encode('ascii', 'ignore')
于 2015-09-24T22:28:56.617 に答える
0

ファイルを編集する場合は、使用されたBOMを確認する必要があります。このバージョンの@ivan_pozdeev回答は、エンコーディングとオプションのBOMの両方を返します。

def encoding_by_bom(path, default='utf-8') -> Tuple[str, Optional[bytes]]:
    """Adapted from https://stackoverflow.com/questions/13590749/reading-unicode-file-data-with-bom-chars-in-python/24370596#24370596 """

    with open(path, 'rb') as f:
        raw = f.read(4)    # will read less if the file is smaller
    # BOM_UTF32_LE's start is equal to BOM_UTF16_LE so need to try the former first
    for enc, boms in \
            ('utf-8-sig', (codecs.BOM_UTF8,)), \
            ('utf-32', (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)), \
            ('utf-16', (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)):
        for bom in boms:
            if raw.startswith(bom):
                return enc, bom
    return default, None

于 2021-08-16T11:12:20.653 に答える