2017/10/04

Python Tips: Python で UTF-8 の BOM ありなしを見分けたい

Python で UTF-8 の BOM のありなしを見分ける方法について見てみたいと思います。

UTF-8 には、「バイト・オーダー・マーク」、通称「 BOM 」と呼ばれるものがあります。これはテキストの始まりをプログラムに伝えるためのデータ内の特定のマークのことであり、具体的にはユニコード文字 U+FEFF がそのマークとして使用されています。

UTF-8 にはこの BOM があるものと無いものとが存在していて、前者を「 BOM あり UTF-8 」( UTF-8 with BOM )、後者を「 BOM なし UTF-8 」あるいはただの「 UTF-8 」と呼んだりします。

詳しくは Wikipedia がわかりやすいので興味のある方はご覧になってみてください。

Byte order mark - Wikipedia


この UTF-8 の BOM を Python で扱う方法について見てみましょう。

ファイルを読む


ファイルを読む場合は、 open() 関数の引数 encoding で指定する文字コードを、 BOM なしの UTF-8 では 'utf-8' 、 BOM あり UTF-8 では 'utf-8-sig' と指定します。

BOM あり UTF-8 をあえて 'utf-8' で読み込むと最初の 1 文字が BOM を表す '\ufeff' (不可視文字)になるので、例えば、 BOM のありなしを自動判定してファイルの中身を読み込む関数を作るとしたら次のようになります。

def open_file_with_utf8(filename):
    '''utf-8 のファイルを BOM ありかどうかを自動判定して読み込む
    '''
    is_with_bom = is_utf8_file_with_bom(filename)

    encoding = 'utf-8-sig' if is_with_bom else 'utf-8'

    return open(filename, encoding=encoding).read()


def is_utf8_file_with_bom(filename):
    '''utf-8 ファイルが BOM ありかどうかを判定する
    '''
    line_first = open(filename, encoding='utf-8').readline()
    return (line_first[0] == '\ufeff')


content = open_file_with_utf8('file_with_utf8.txt')


ファイルを書く


逆にファイルを書くときも encoding を指定すれば OK です。 BOM なし UTF-8 として書きたければ 'utf-8' を、 BOM あり UTF-8 にしたければ 'utf-8-sig' を指定します。

with open('file_out_with_utf8_with_bom', 'w') as f:
    f.write('This is a file written with utf-8 with BOM.')

ちなみに、上述のとおり、 BOM あり UTF-8 のファイルを encoding 'utf-8' で読むと、最初の文字が '\ufeff' になります。逆に、 BOM なし UTF-8 のファイルを encoding 'utf-8-sig' で読むと、普通に読めて特に問題はありません。

BOM のありなしが特に問題にならない場合はよいのですが、ファイルの長さなどを厳密に比較したりしたいような場合には 'utf-8-sig' を適宜使うとよいでしょう。


参考


utf 8 - python utf-8-sig BOM in the middle of the file when appending to the end - Stack Overflow
7.2. codecs — Codec registry and base classes — Python 3.6.3 documentation

0 件のコメント: