エラーの内容
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() だけでは不十分な場合に特に便利です。

