PythonのUnicodeDecodeError: 'utf-8' codec can't decode byteを修正する方法

beginner🐍 Python2026-03-22| Python 3.x、Windows/Linux/macOS — ファイルの読み込みや外部データの処理時に最もよく発生する

Error Message

UnicodeDecodeError: 'utf-8' codec can't decode byte
#python#unicode#エンコーディング

エラーの内容

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x?? in position ??: invalid start byte

ファイル読み込み時の完全なエラー例:

Traceback (most recent call last):
  File "read.py", line 3, in <module>
    content = f.read()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb2 in position 14: invalid start byte

原因

Pythonはデフォルトでコーデックに UTF-8 を使用します。ファイルが別のエンコーディングで保存されていた場合、デコーダーは最初に認識できないバイトに遭遇した時点でエラーを起こします。上記の例では 0xb2 というバイトは Latin-1 では有効ですが、UTF-8 では不正なバイトです。ほとんどの場合、原因は以下のいずれかです:

  • Excelエクスポートで非常によく見られる、Windows-1252(CP1252) または Latin-1(ISO-8859-1) で保存された CSV/テキストファイル
  • ANSI エンコーディングをデフォルトとするWindows上で作成されたファイル
  • テキストモードで誤って開かれたバイナリファイル
  • サーバーがヘッダーで宣言されたものと異なる文字セットで送信する HTTP レスポンス
  • latin1 文字セットを使用した MySQL からのデータベースダンプ

修正方法1:実際のエンコーディングを先に検出する

推測に頼らず、chardet を使用して検出しましょう。常に100%正確とは限りませんが、試行錯誤を大幅に省けるほど高い精度を持っています:

pip install chardet
import chardet

with open('data.csv', 'rb') as f:
    raw = f.read()
    result = chardet.detect(raw)
    print(result)  # {'encoding': 'Windows-1252', 'confidence': 0.73, 'language': ''}

検出されたエンコーディングを open() に直接渡します:

with open('data.csv', encoding='windows-1252') as f:
    content = f.read()

修正方法2:一般的なエンコーディングを手動で試す

chardet が使えない場合でも、以下の5つのエンコーディングで実際のファイルの約95%はカバーできます。順番に試してみてください:

encodings = ['utf-8', 'windows-1252', 'latin-1', 'utf-16', 'cp1250']

for enc in encodings:
    try:
        with open('data.txt', encoding=enc) as f:
            content = f.read()
        print(f'Works with: {enc}')
        break
    except UnicodeDecodeError:
        continue

修正方法3:errors パラメータをフォールバックとして使用する

レガシーシステムやベンダー、管理外のアップロードからのファイルなど、エンコーディングを変更できない場合があります。そのようなケースでは、クラッシュする代わりに不正なバイトに対する処理方法を Python に指示します:

# Option A: 不正なバイトを完全にスキップする
with open('data.txt', encoding='utf-8', errors='ignore') as f:
    content = f.read()

# Option B: 不正なバイトを(Unicode置換文字)に置き換える
with open('data.txt', encoding='utf-8', errors='replace') as f:
    content = f.read()

# Option C: デバッグ用に backslashreplace を使用 — 失敗したバイトを正確に表示
with open('data.txt', encoding='utf-8', errors='backslashreplace') as f:
    content = f.read()

警告: errors='ignore' は文字を無警告で削除します。データの欠損が許容できる場合にのみ使用してください。氏名、住所、その他の自由記述フィールドはこの処理で正しく保持されないことがほとんどです。

修正方法4:バイナリとして読み込み、手動でデコードする

chardet の推測が外れた場合や、処理前に生のバイトを調べる必要がある場合に有効です:

with open('data.txt', 'rb') as f:
    raw_bytes = f.read()

# 検出または既知のエンコーディングでデコード
content = raw_bytes.decode('windows-1252')

# 後続処理のために UTF-8 に再エンコードする場合
content_utf8 = raw_bytes.decode('windows-1252').encode('utf-8').decode('utf-8')

修正方法5:HTTPレスポンス

APIやスクレイピングしたページは注意が必要です。Content-Type ヘッダーが UTF-8 と示していても、実際のボディが Latin-1 であることがあります。requests ライブラリにはいくつかの対処方法があります:

import requests

response = requests.get('https://example.com/data')

# Option A: ボディからエンコーディングを推測させる(内部でchardetを使用)
response.encoding = response.apparent_encoding
text = response.text

# Option B: 既知のエンコーディングを強制指定する
response.encoding = 'windows-1252'
text = response.text

# Option C: 生のバイトを取得して自分でデコードする
text = response.content.decode('latin-1')

修正方法6:pandas の read_csv

このエラーが最も頻繁に発生する場所です。Windows上でExcelからCSVにエクスポートすると、ファイルがExcel上で正常に見える場合でも、ほぼ必ず UTF-8 ではなく Windows-1252 が使用されます:

import pandas as pd

# エンコーディングを直接指定する
df = pd.read_csv('export.csv', encoding='windows-1252')

# エンコーディングが不明な場合、不正なバイトを置換して後で確認する
df = pd.read_csv('export.csv', encoding_errors='replace')

# latin-1 は256のバイト値すべてを受け入れるため、UnicodeDecodeError が発生しない
df = pd.read_csv('export.csv', encoding='latin-1')

修正方法7:Python 2 → Python 3 への移行

古い Python 2 のコードは文字列を生のバイトとして扱っていました。Python 3 への移行では明示的な指定が必要です。両バージョンで動作する必要があるコードには io.open() を使用してください:

import io

with io.open('legacy.txt', encoding='utf-8') as f:
    content = f.read()

修正の確認

本番環境にリリースする前に、簡単な動作確認を行いましょう。間違ったエンコーディングによる文字化けは、必ずしもクラッシュとして現れるわけではないため、気づきにくい場合があります:

with open('data.txt', encoding='windows-1252') as f:
    content = f.read()

# repr() は問題が残っている場合に \x?? シーケンスを表示する
print(repr(content[:200]))
print(len(content))  # 期待されるファイルサイズと一致するはず

# 既知のコンテンツが含まれていることを確認する
assert 'expected_string' in content, 'エンコーディング不一致 — 文字化けが発生しています'

予防策

以下の習慣を身につければ、このエラーに悩まされることはほとんどなくなります:

  • ファイルを開く際は必ずエンコーディングを指定する。 システムのデフォルトには頼らないでください。Windowsでは CP1252、macOSでは UTF-8、一部の Linux サーバーでは ASCII がデフォルトになっており、環境が変わった瞬間にコードが壊れます。

悪い例

with open('file.txt') as f: ...

良い例

with open('file.txt', encoding='utf-8') as f: ...

  
  - **最初から UTF-8 でファイルを保存する。** VS Code では画面右下のステータスバーにエンコーディングが表示されます。クリックすると変更できます。今の1分が、後の1時間のデバッグを節約します。
  - **システムの境界でバリデーションを行う。** アップロード、SFTP、またはサードパーティAPIから受け取るファイルは、バッチジョブの途中ではなく、受信時点でエンコーディングを確認すべきです。
  - **パイプ出力には `PYTHONIOENCODING` を設定する。** 標準出力に書き込むスクリプトは、別のプロセスにパイプされるとエンコーディング問題が発生することがあります:
    ```
PYTHONIOENCODING=utf-8 python myscript.py

エンコーディング早見表

  • utf-8 — 世界標準;データを自分で管理できる場合はこれを使用する
  • windows-1252 / cp1252 — Windows 西ヨーロッパ言語用;Excel CSV エクスポートのデフォルト
  • latin-1 / iso-8859-1 — 256のバイト値すべてをマップ;UnicodeDecodeError が発生しないため最終手段として有用
  • utf-16 — Windows メモ帳の「Unicode」保存形式;先頭にBOMヘッダーがある
  • shift_jis / cp932 — Windowsシステムからの日本語テキスト

ターミナルが独自のエンコーディングで出力を文字化けさせている場合、ブラウザで生のバイトを調べると役立つことがあります。toolcraft.app の Base64 エンコーダー/デコーダーを使えば、生のバイトを貼り付けて内容を正確に確認できます。repr() だけでは不十分な場合に特に便利です。

Related Error Notes