【Python】テキストファイルの文字コードを判別してファイルを開く

Pythonのopenで使用するencodingはプラットフォームに依存します。
Windowsであればcp932です。

https://docs.python.jp/3/library/functions.html#open

Windows上のUTF-8で保存されたテキストファイルをencoding引数に何もつけずにopenするとエラーが発生します。

with open('UTF-8.txt') as f:
    s = f.read()
    print(s) # UnicodeDecodeError: 'cp932' codec can't decodeが発生する

テキストファイルを開く前に文字コードを判別してから適切なencoding引数で開くようにしてみます。

chardetを使用して文字コードを判別する

chardetを使用して、文字コードを判別します。

https://pypi.org/project/chardet/

pipを使用してchardetをインストールします。

pip install chardet

UTF-8とShift-JISの文字コードのテキストファイルを用意して、それぞれ下記の内容でファイルを保存しました。

あいうえお

このテキストファイルをchardetのdetectメソッドを使用してテキストファイルの文字コードを判別してみます。

detectメソッドの引数にはbytesまたはbytearrayを渡す必要があります。
openでファイルを開くときにmodeの引数にrbを指定し、バイナリモードで読み込んだファイルを detectの引数として使用します。

import chardet

with open('UTF-8.txt', 'rb') as f:
    print('UTF-8.txt')
    print(chardet.detect(f.read()))

print()
with open('Shift-JIS.txt', 'rb') as f:
    print('Shift-JIS.txt')
    print(chardet.detect(f.read()))

ファイルの文字コード、文字コードの確度、ファイルに書かれている言語を表示してくれます。

UTF-8.txt
{'encoding': 'utf-8', 'confidence': 0.9690625, 'language': ''}

Shift-JIS.txt
{'encoding': 'Windows-1252', 'confidence': 0.73, 'language': ''}

UTF-8のファイルは判別できていますが、Shift-JISのファイルは間違った文字コードとして判別しています。 bytesの内容から判別を行っているようですがbytesの元となるテキストファイルの内容によっては うまく判別できないようです。

青空文庫(夏目漱石の「こころ」)のファイルを利用してテストしてみたところ正常にUTF-8とShift-JISと判別できました。

https://www.aozora.gr.jp/cards/000148/card773.html

import chardet

# 文字コードUTF-8の「こころ」テキストファイル
with open('kokoro_UTF-8.txt', 'rb') as f:
    print('kokoro_UTF-8.txt')
    print(chardet.detect(f.read()))

print()

# 文字コードShift-JISの「こころ」テキストファイル
with open('kokoro_Shift-JIS.txt', 'rb') as f:
    print('kokoro_Shift-JIS.txt')
    print(chardet.detect(f.read()))
kokoro_UTF-8.txt
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

kokoro_Shift-JIS.txt
{'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}</pre>

文字コードを判別してからテキストファイルを開く

chardetのマニュアルに複数のファイルの文字コードを判別する方法が掲載されています。
こちらのコードを応用して、事前に文字コードを判別してからテキストファイルを開いてみます。

https://chardet.readthedocs.io/en/latest/usage.html#example-detecting-encodings-of-multiple-files

import glob
from chardet.universaldetector import UniversalDetector


def detect_character_code(pathname):
    """
    pathnameから該当するファイルの文字コードを判別して
    ファイル名と文字コードのdictを返す

    :param pathname: 文字コードを判別したいフォルダ
    :return: ファイル名がキー、文字コードが値のdict
    """
    files_code_dic = {}
    detector = UniversalDetector()
    for file in glob.glob(pathname):
        with open(file, 'rb') as f:
            detector.reset()
            for line in f.readlines():
                detector.feed(line)
                if detector.done:
                    break
            detector.close()
            files_code_dic[file] = detector.result['encoding']
    return files_code_dic


if __name__ == '__main__':
    # カレントフォルダ内のテキストファイルの文字コードを判別
    path = './'
    filename = '*'
    extension = 'txt'
    pathname = path + filename + '.' + extension
    files_code = detect_character_code(pathname)
    for filename in glob.glob(pathname):
        with open(filename, encoding=files_code[filename]) as f:
            for line in f.readlines():
                print(filename)
                print(line)
                break

コードを動かすとカレントフォルダ内のテキストファイルの文字コードを判別してからテキストファイルを開きます。 開いたファイルの名前と、ファイルの内容の1行目を表示します。

.\kokoro_Shift-JIS.txt
こころ

.\kokoro_UTF-8.txt
こころ

chardetは非常に便利ですが、ファイルの内容によっては文字コードを誤認識する可能性があります。 文字コードの確度が低い場合は、手動で文字コードを指定してからファイルを読み込む必要がでてきます。

Licensed under CC BY-NC-SA 4.0
最終更新 2019年1月17日 11:26
Hugo で構築されています。
テーマ StackJimmy によって設計されています。