PythonでBOMを使用するUTF-8をBOMを使用しないUTF-8に変換する


82

ここに2つの質問があります。私は通常BOMを備えたUTF-8であるファイルのセットを持っています。それらを(理想的には適切に)BOMなしのUTF-8に変換したいと思います。これcodecs.StreamRecoder(stream, encode, decode, Reader, Writer, errors)を処理するようです。しかし、私は実際に使用法の良い例を見ていません。これはこれを処理するための最良の方法でしょうか?

source files:
Tue Jan 17$ file brh-m-157.json 
brh-m-157.json: UTF-8 Unicode (with BOM) text

また、明示的に知らなくても(ASCIIとUTF-16で見られる)さまざまな入力エンコーディングを処理できれば理想的です。これはすべて実行可能であるように思われます。既知のPythonエンコーディングを取得してBOMなしでUTF-8として出力できるソリューションはありますか?

下から提案された1つのsol'nを編集します(ありがとう!)

fp = open('brh-m-157.json','rw')
s = fp.read()
u = s.decode('utf-8-sig')
s = u.encode('utf-8')
print fp.encoding  
fp.write(s)

これにより、次のエラーが発生します。

IOError: [Errno 9] Bad file descriptor

ニュース速報

コメントで、「r +」/「r + b」ではなく「rw」モードでファイルを開くのが間違っていると言われているので、最終的に質問を再編集して、解決した部分を削除する必要があります。


2
読み取りと更新のために、つまりr+モードを使用してファイルを開く必要があります。b面白いラインエンディングビジネスなしでWindowsでも動作するように追加します。最後に、ファイルの最初に戻って、最後に切り捨てることをお勧めします。更新された回答を参照してください。
マーティンガイスラー2012年

回答:


125

「utf-8-sig」コーデックを使用するだけです。

fp = open("file.txt")
s = fp.read()
u = s.decode("utf-8-sig")

これによりunicode、BOMなしの文字列が得られます。その後、使用することができます

s = u.encode("utf-8")

通常のUTF-8でエンコードされた文字列をに戻しsます。ファイルが大きい場合は、すべてをメモリに読み込むことは避けてください。BOMはファイルの先頭が3バイトであるため、次のコードを使用してファイルからBOMを取り除くことができます。

import os, sys, codecs

BUFSIZE = 4096
BOMLEN = len(codecs.BOM_UTF8)

path = sys.argv[1]
with open(path, "r+b") as fp:
    chunk = fp.read(BUFSIZE)
    if chunk.startswith(codecs.BOM_UTF8):
        i = 0
        chunk = chunk[BOMLEN:]
        while chunk:
            fp.seek(i)
            fp.write(chunk)
            i += len(chunk)
            fp.seek(BOMLEN, os.SEEK_CUR)
            chunk = fp.read(BUFSIZE)
        fp.seek(-BOMLEN, os.SEEK_CUR)
        fp.truncate()

ファイルを開き、チャンクを読み取り、読み取った場所より3バイト早くファイルに書き込みます。ファイルはインプレースで書き換えられます。より簡単な解決策は、newtoverの回答のように短いファイルを新しいファイルに書き込むことです。それは簡単ですが、短期間に2倍のディスク容量を使用します。

エンコーディングを推測することに関しては、エンコーディングを最も具体的なものから最も具体的でないものへとループすることができます。

def decode(s):
    for encoding in "utf-8-sig", "utf-16":
        try:
            return s.decode(encoding)
        except UnicodeDecodeError:
            continue
    return s.decode("latin-1") # will always work

UTF-16でエンコードされたファイルはUTF-8としてデコードされないため、最初にUTF-8を試してみます。それが失敗した場合は、UTF-16を試してみます。最後に、Latin-1を使用します— 256バイトはすべてLatin-1の有効な値であるため、これは常に機能します。Noneこれは実際にはフォールバックであり、コードでこれをより慎重に処理する必要がある場合があるため、この場合は代わりに戻ることをお勧めします(可能な場合)。


うーん、編集#1の質問をサンプルコードで更新しましたが、ファイル記述子が正しくありません。助けを求めてthx。これを理解しようとしています。
timpone 2012年

64

Python 3では、非常に簡単です。ファイルを読み取り、utf-8エンコーディングを使用して書き直します。

s = open(bom_file, mode='r', encoding='utf-8-sig').read()
open(bom_file, mode='w', encoding='utf-8').write(s)

3
このトピックに関するWebのベストアンサー。utf-8-sigを使用するだけです。
qtRoS 2017年

6
import codecs
import shutil
import sys

s = sys.stdin.read(3)
if s != codecs.BOM_UTF8:
    sys.stdout.write(s)

shutil.copyfileobj(sys.stdin, sys.stdout)

このコードがどのように機能するか説明できますか?$ remove_bom.py <input.txt> output.txt私は正しいですか?
guneysus 2013年

@guneysus、はい、正確に
newtover 2013年

1
追加しましたheader = header[3:] if header[0:3] == codecs.BOM_UTF8 else header
chinmayv 2014

5

これは、あらゆる種類のエンコーディングをBOMなしでUTF-8に変換し、ウィンドウのenlineをユニバーサル形式に置き換えるための私の実装です。

def utf8_converter(file_path, universal_endline=True):
    '''
    Convert any type of file to UTF-8 without BOM
    and using universal endline by default.

    Parameters
    ----------
    file_path : string, file path.
    universal_endline : boolean (True),
                        by default convert endlines to universal format.
    '''

    # Fix file path
    file_path = os.path.realpath(os.path.expanduser(file_path))

    # Read from file
    file_open = open(file_path)
    raw = file_open.read()
    file_open.close()

    # Decode
    raw = raw.decode(chardet.detect(raw)['encoding'])
    # Remove windows end line
    if universal_endline:
        raw = raw.replace('\r\n', '\n')
    # Encode to UTF-8
    raw = raw.encode('utf8')
    # Remove BOM
    if raw.startswith(codecs.BOM_UTF8):
        raw = raw.replace(codecs.BOM_UTF8, '', 1)

    # Write to file
    file_open = open(file_path, 'w')
    file_open.write(raw)
    file_open.close()
    return 0

3

私はこの質問を見つけました configparser.ConfigParser().read(fp)UTF8 BOMヘッダーのあるファイルを開くときにた。

ConfigPhaserが次のエラーを報告する代わりに構成ファイルを開くことができるようにヘッダーを削除する解決策を探している人は File contains no section headers、次のようにファイルを開いてください。

configparser.ConfigParser().read(config_file_path, encoding="utf-8-sig")

これにより、ファイルのBOMヘッダーを削除する必要がなくなるため、多大な労力を節約できます。

(これは無関係に聞こえますが、うまくいけば、これは私のように苦労している人々を助けることができます。)


1
私が最初にtryで作業していたので、ただし->これにより、UTF-8の「BOMではない」エンコードファイルも問題なく開きます
flipSTAR 2010年

2

コーデックを使用できます。

import codecs
with open("test.txt",'r') as filehandle:
    content = filehandle.read()
if content[:3] == codecs.BOM_UTF8:
    content = content[3:]
print content.decode("utf-8")

まったく使用できないスニペット(filehandle?codecs.BOM_UTF8も構文エラーを返します)
Max
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.