Python UnicodeDecodeError: 'charmap' コーデックがWindowsでバイトをデコードできない問題を修正

beginner🪟 Windows2026-07-04| Windows 10/11上のPython 3.x、open()でテキストファイルを読み込む際に発生

Error Message

UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 10859: character maps to <undefined>
#python#unicode#エンコーディング#charmap#utf-8#windows

エラーの内容

WindowsでPythonを使ってテキストファイルを読み込もうとしたとき、突然このエラーが発生することがあります:

UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 10859: character maps to <undefined>

メモ帳やVS Codeでは問題なく開けるのに、Pythonは拒否します。原因はひとつ:エンコーディングのデフォルト設定です。

根本原因

WindowsでPythonのopen()を使うと、システムロケールのエンコーディング(通常はcp1252、Windows-1252とも呼ばれ、charmapとも称される)がデフォルトになります。これはラテン文字の一部しかカバーしていません。

ファイルにその範囲外のバイト(UTF-8テキスト、別コードページのファイル、絵文字や拡張Unicodeを含むもの)が含まれていると、PythonはUnicodeDecodeErrorをスローします。バイト値0x9dはその典型例で、UTF-8では有効ですがcp1252では未定義です。

macOSやLinuxではほとんど発生しません。それらのデフォルトはすでにUTF-8です。Windowsが例外なのです。

修正方法1:UTF-8エンコーディングを明示する(9割のケースで解決)

open()の呼び出しにencoding='utf-8'を追加するだけです:

# 修正前(Windowsでエラーになる)
with open('data.txt') as f:
    content = f.read()

# 修正後(どこでも動作する)
with open('data.txt', encoding='utf-8') as f:
    content = f.read()

ファイルがUTF-8で保存されている場合(現代のほとんどのツールのデフォルト)、これだけで済みます。1行の変更です。

動作確認

with open('data.txt', encoding='utf-8') as f:
    content = f.read()
print(f"Read {len(content)} characters successfully")

例外は発生しません。修正完了です。

修正方法2:エンコーディングが不明な場合

クライアントやサードパーティシステムからファイルを受け取る場合、エンコーディングはcp1252、latin-1、shift-jis、GBKなど何でもありえます。chardetを使って自動検出しましょう。

pip install chardet
import chardet

# まず生のバイト列として読み込む
with open('data.txt', 'rb') as f:
    raw = f.read()

result = chardet.detect(raw)
detected_encoding = result['encoding']
confidence = result['confidence']
print(f"Detected: {detected_encoding} (confidence: {confidence:.0%})")

# 検出したエンコーディングで読み込む
with open('data.txt', encoding=detected_encoding) as f:
    content = f.read()

信頼度が70%未満の場合、ファイルが破損しているか、エンコーディングが混在しているか、非常にマイナーなコードページが使われている可能性があります。結果を処理する前に送信者に確認してください。

修正方法3:問題のあるバイトをスキップまたは置換する

ファイルを素早く読み込む必要があり、一部の読めない文字を気にしない場合は、errorsパラメータで対処できます:

# デコードできないバイトを置換文字(U+FFFD)に置き換える
with open('data.txt', encoding='utf-8', errors='replace') as f:
    content = f.read()

# または完全に除去する
with open('data.txt', encoding='utf-8', errors='ignore') as f:
    content = f.read()

注意点:errors='ignore'はデータを無警告で破棄します。顧客データや財務エクスポートを処理している場合、何が失われたか永遠にわかりません。読めない文字が本当に重要でないケースに限定して使用してください。

修正方法4:プロセス全体でUTF-8を強制する

ファイル操作が散在するスクリプトを実行する場合は、PYTHONUTF8=1を一度設定するだけで、すべてのopen()呼び出しが自動的にUTF-8をデフォルトにします:

# コマンドプロンプト
set PYTHONUTF8=1
python your_script.py

# PowerShell
$env:PYTHONUTF8 = "1"
python your_script.py

または-Xフラグでインライン指定もできます:

python -X utf8 your_script.py

UTF-8モードはプロセス全体(ファイルI/O、stdin、stdout、stderr、すべて)に適用されます。Python 3.7以降で利用可能です。

永続的に設定するには、Windowsの環境変数にPYTHONUTF8=1を追加します:システムのプロパティ → 詳細設定 → 環境変数 → 新規(「ユーザー環境変数」の下)。

修正方法5:古いWindowsファイルにはlatin-1を使う

古いAccessデータベース、レガシーCRM、政府のデータポータルからのエクスポートなど、一部のレガシーファイルは実際にcp1252またはlatin-1です。chardetがこれを確認した場合は、適切なコーデックで読み込みます:

with open('legacy_file.txt', encoding='cp1252') as f:
    content = f.read()

# latin-1はバイト値0x00-0xFFすべてをUnicode文字にマッピングする
# そのためUnicodeDecodeErrorが発生しない
with open('mystery_file.txt', encoding='latin-1') as f:
    content = f.read()

latin-1はすべてのバイト値をエラーなしで受け入れます。それがフォールバックとして便利な理由です。ただし、ファイルが実際にUTF-8の場合、出力は文字化けします。最後の手段として使い、最初には使わないでください。

システムのデフォルトエンコーディングを確認する

お使いのマシンでPythonが何をデフォルトにしているか確認するには:

import sys
import locale

print(sys.getdefaultencoding())      # 通常は 'utf-8'
print(sys.getfilesystemencoding())   # Mac/Linuxは 'utf-8'、Windowsは 'mbcs'
print(locale.getpreferredencoding()) # open() が実際に使うもの — よく 'cp1252'

locale.getpreferredencoding()cp1252を返す場合、それが原因です。明示的なencoding=指定のないすべてのopen()呼び出しが、警告なしにそれを使用します。

予防策

  • すべてのopen()呼び出しに必ずencoding='utf-8'を書く。UTF-8がデフォルトのLinuxやmacOS上でも書くようにしましょう。コードはいつかWindowsで実行されることになります。
  • エディタでファイルをUTF-8で保存する。VS CodeはデフォルトでUTF-8です。Windows 10・11のメモ帳もUTF-8がデフォルトになりましたが、古いバージョンはANSI/cp1252がデフォルトでした。
  • ライブラリを公開する場合は、ファイルを読み込む関数にencodingパラメータを受け付けるようにし、デフォルト値をドキュメントに明記してください。
  • PylintのW1514unspecified-encoding)は、明示的なエンコーディングのないすべてのopen()にフラグを立てます。CIパイプラインに組み込めば、本番環境に届く前に問題を検出できます。

Related Error Notes