別環境で作られたテキストファイルの内容を読み込む際には、まずそのエンコーディングを調べる必要がある。chardetパッケージを使って、これを行う方法を紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
# chardetによるエンコーディングの判定とテキストデータのデコード
# sjis.txtの内容:このファイルはシフトJISでエンコーディングされています
from chardet import detect # 「pip install chardet」などでインストールしておく
with open('sjis.txt', 'rb') as f: # バイナリファイルとしてファイルをオープン
b = f.read() # ファイルの内容を全て読み込む
print(b) # b'\x82\xb1\x82\xcc\x83t\x83@\x83C……\xa2\x82\xdc\x82\xb7\r\n'
enc = detect(b) # chardet.detect関数を使ってエンコーディングを判定
print(enc)
# 出力結果:
# {'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}
# 得られたエンコーディング情報を使ってファイルをオープンし直す
with open('sjis.txt', encoding=enc['encoding']) as f:
s = f.read()
print(repr(s)) # 'このファイルはシフトJISでエンコーディングされています\n'
# もしくは得られたエンコーディング情報を使ってバイト列をデコード
s = b.decode(encoding=enc['encoding'])
print(repr(s)) # 'このファイルはシフトJISでエンコーディングされています\r\n'
# ファイルサイズが大きい場合
from chardet.universaldetector import UniversalDetector
with open('sjis.txt', 'rb') as f: # ファイルをバイナリファイルとしてオープン
detector = UniversalDetector() # UniversalDetectorオブジェクトを生成
for line in f: # 行末(\n)またはEOFまでを読み込みながら、以下を繰り返す
detector.feed(line) # 読み込んだデータをfeedメソッドに渡す
if detector.done: # 判定できたらdone属性がTrueになるのでループを終了
break
detector.close() # ループ終了時にUniversalDetectorオブジェクトをクローズ
print(detector.result)
# 出力結果:
# {'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}
# UniversalDetectorオブジェクトもまとめてwith文で取り扱う
from chardet.universaldetector import UniversalDetector
from contextlib import closing
with open('sjis.txt', 'rb') as f, closing(UniversalDetector()) as detector:
for line in f:
detector.feed(line)
if detector.done:
break
print(detector.result) # 結果を出力
Pythonのopen関数でテキストファイルをオープンする場合、特に指定をしない限り、コードを実行しようとしているプラットフォームごとに定められているエンコーディングを使って、そのファイルがオープンされる。
ローカルマシンのハードディスクなどを対象として、決まったエンコーディングでファイルを読み書きしている分には問題はないが、別のマシンで作られたテキストファイルを読み書きしたり、ネットワーク経由で手に入れたテキストデータを扱ったりする際には、エンコーディングが手元のマシンのそれとは異なる場合がある。
例えば、以下はシフトJISでエンコードされたテキストファイル(sjis.txt)の内容をmacOSで読み込もうとしているところだ(ファイルの内容は「このファイルはシフトJISでエンコーディングされています\n」)。
with open('sjis.txt') as f: # sjis.txtファイルはシフトJISでエンコードされている
content = f.read()
これを実行した結果を以下に示す。
この通り、macOS上で特に指定をせずにシフトJISエンコーディングのテキストファイルの内容を読み込もうとすると失敗する(ファイルはオープンできるが、readメソッドで例外が発生する)。Windows環境(デフォルトのエンコーディングは「cp932」≒シフトJIS)でUTF-8エンコーディングのテキストファイルの内容を読み込もうとしたときにも同様なエラーが発生する。
テキストファイルで使われているエンコーディングが分かっていれば、open関数のencodingパラメーターに'shift_jis'(や'UTF-8')などを渡すことでファイルの内容を読み込めるようになるが(「[解決!Python]エンコーディングを指定して、シフトJISなどのファイルを読み書きするには」を参照)、エンコーディングが分からないときには、それを判定する必要がある。
chardetパッケージはまさにこれを行ってくれる。ただし、Pythonに標準で添付されるパッケージではないので、「pip install chardet」などとしてあらかじめインストールしておく必要がある。
chardetパッケージの最も簡単な使い方は、エンコーディングが不明のテキストファイルを「バイナリファイルとして」オープンして、それをこのパッケージが提供するdetect関数に渡すことだ。以下に例を示す。
# 「pip install chardet」などを実行して、chardetパッケージをインストールしておく
from chardet import detect # chardetパッケージからdetect関数をインポート
with open('sjis.txt', 'rb') as f: # バイナリファイルとして読み込みオープン
b = f.read() # ファイルから全データを読み込み
enc = detect(b) # 読み込んだデータdetect関数に渡して判定する
print(enc)
# 出力結果:
#{'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}
detect関数の戻り値は辞書であり、この関数によって推測されたエンコーディングは'encoding'キーの値となっている。上の例を見ると分かる通り、確度('confidence'キー)と言語('language'キー)も戻される。この場合は、エンコーディングはシフトJISで、その確度は99%ということだ。
エンコーディングが分かったら、それをopen関数に指定して、テキストファイルを再度オープンすると、そのエンコーディングを使ってファイルの読み書きが行われるようになる。
with open('sjis.txt', encoding=enc['encoding']) as f:
s = f.read()
print(repr(s)) # 'このファイルはシフトJISでエンコーディングされています\n'
あるいは、全てのデータを既に読み込み済みなので、バイト列のdecodeメソッドにエンコーディングを指定する方法もある。
s = b.decode(enc['encoding'])
print(repr(s)) # 'このファイルはシフトJISでエンコーディングされています\r\n'
2つの方法の出力結果を見ると分かるが、バイト列として読み込んだテキストデータをdecodeメソッドでデコードしたときには、改行文字がプラットフォーム固有の値のままである点には注意が必要だ(Windowsでは「\r\n」、macOSやLinuxでは「\n」)。そのため、後者の方法でデコードしたときには、文字列のreplaceメソッドなどで改行文字を変換する必要があるかもしれない(エンコーディングを指定してオープンし直した場合は、デフォルトで改行コードは自動的に変換される)。
ファイルサイズが大きな場合に、エンコーディングを調べるためだけに全ての内容を読み込むのは無駄が大きいと考えるかもしれない。そのようなときには、chardetパッケージが提供するUniversalDetectorクラスを使用する。
Copyright© Digital Advantage Corp. All Rights Reserved.