できない.dev

Python で「UnicodeDecodeError」を解消できずファイルが読めない

テキストモードでファイルを開く際の既定エンコーディングが実ファイルと違うことが原因。
open() に encoding 引数を明示するか、バイナリモードで読んで自前デコードすると解決する。

公開: 更新:

要約

Python で UnicodeDecodeError: 'utf-8' codec can't decode byte ... などが出る場合、ファイルの実エンコーディングと open() が想定するエンコーディングが食い違っている。open(path, encoding='utf-8') のように明示的に指定する、または utf-8-sig / cp932 などファイル実態に合わせれば解消する。

よくある原因

  1. encoding 未指定: open(path) だけだと OS のロケールに依存する。
    Windows では cp932(Shift_JIS 系)、Linux では UTF-8 であることが多い。
  2. BOM 付き UTF-8: Excel で保存した CSV などは BOM が先頭に付き、utf-8 で読むと最初の文字に余計なバイトが残ることがある。
  3. エンコーディング誤認: 古い Windows 由来の文書を utf-8 として開いてしまう。
  4. 破損バイト: ネットワーク越しのバイナリ混入や、途中で切れたファイルで一部のバイト列が無効。

解決策

1. encoding を明示する

ほぼ全ての UnicodeDecodeError はこれで防げる。

with open("data.txt", encoding="utf-8") as f:
    text = f.read()

open() の仕様(新しいタブで開く) のとおり、テキストモードで encoding を省略するとロケール既定が使われる。

2. BOM 付き UTF-8 は utf-8-sig

import csv
with open("export.csv", encoding="utf-8-sig") as f:
    rows = list(csv.reader(f))

utf-8-sig は読み込み時に BOM を取り除き、書き込み時には先頭に付与する。

3. Shift_JIS / cp932

Windows のメモ帳で保存した古いファイルは cp932 が安全。

with open("legacy.txt", encoding="cp932") as f:
    text = f.read()

shift_jis ではなく cp932 を推奨(NEC 拡張・IBM 拡張文字を含むため)。

4. errors 引数で握り潰す

ログのような壊れても致命的でないファイルなら、errors='replace' でデコードできないバイトを U+FFFD(�)に置き換えられる(? への置換はエンコード時の挙動で、デコード時は �)。

with open("noisy.log", encoding="utf-8", errors="replace") as f:
    text = f.read()

5. バイナリモードで読み自前デコード

判別が必要なら chardetcharset-normalizer でエンコーディングを推定して decode する。

import charset_normalizer
raw = open("unknown.txt", "rb").read()
guess = charset_normalizer.from_bytes(raw).best()
text = str(guess)

実行例

実際に上記の手順を python:3.12(Debian GNU/Linux 13 trixie)環境で動かすと、cp932 で用意したファイルを encoding 未指定で開いた段階で UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 が発生し、encoding='cp932' を明示することで正常に読み出せることが確認できる。

$ python bad.py
Traceback (most recent call last):
  File "/tmp/tmp.1PbIWpiSpn/bad.py", line 2, in <module>
    print(f.read())
          ^^^^^^^^
  File "<frozen codecs>", line 322, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 0: invalid start byte
終了コード: 1
$ python solve_cp932.py
日本語テキスト
$ python solve_replace.py
'���{��e�L�X�g\n'
$ python solve_utf8_sig.py
utf-8-sig: 'hello'
utf-8    : '\ufeffhello'

— 2026-08-02 時点の出力

この記事は役立ちましたか?