ファイルのエンコーディング問題と文字化けの対処法

ファイルの読み書きでエンコーディングを正しく扱わないと、文字化けやエラーが発生する。日本語を扱う際は特に注意が必要だ。

エンコーディングとは

エンコーディングは、文字をバイト列に変換する方式だ。同じテキストでもエンコーディングが違えばバイト列が変わる。

text = '日本語'

print(text.encode('utf-8'))      # b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'
print(text.encode('shift_jis'))  # b'\x93\xfa\x96{\x8c\xea'
print(text.encode('euc_jp'))     # b'\xc6\xfc\xcb\xdc\xb8\xec'

ファイルを開くときにエンコーディングを指定する

open() でファイルを開くとき、encoding を指定しないとシステムのデフォルトが使われる。

# ❌ エンコーディングを指定していない
with open('data.txt') as f:
    content = f.read()  # 環境によって文字化けする可能性

UTF-8 を明示的に指定するのが安全だ。

# ✅ エンコーディングを明示
with open('data.txt', encoding='utf-8') as f:
    content = f.read()

UnicodeDecodeError の対処

ファイルのエンコーディングと指定したエンコーディングが一致しないとエラーになる。

# Shift_JIS のファイルを UTF-8 で開くとエラー
with open('sjis_file.txt', encoding='utf-8') as f:
    content = f.read()  # UnicodeDecodeError

errors パラメータでエラー処理を変更できる。

# エラーを無視
with open('data.txt', encoding='utf-8', errors='ignore') as f:
    content = f.read()

# 不正な文字を置換
with open('data.txt', encoding='utf-8', errors='replace') as f:
    content = f.read()  # 不正な文字は � に置換される

ファイルのエンコーディングを検出する

chardet ライブラリでエンコーディングを推測できる。

pip install chardet
import chardet

def detect_encoding(filepath):
    with open(filepath, 'rb') as f:
        raw = f.read()
    result = chardet.detect(raw)
    return result['encoding'], result['confidence']

encoding, confidence = detect_encoding('unknown.txt')
print(f'{encoding} (信頼度: {confidence:.0%})')

検出結果を使ってファイルを読み込む。

import chardet

def read_with_autodetect(filepath):
    with open(filepath, 'rb') as f:
        raw = f.read()
    
    detected = chardet.detect(raw)
    encoding = detected['encoding'] or 'utf-8'
    
    return raw.decode(encoding)

content = read_with_autodetect('unknown.txt')

よくあるエンコーディング問題

状況原因対処
Windows で作成したファイルが Linux で文字化けShift_JIS と UTF-8 の混在エンコーディングを明示的に指定
Excel の CSV が文字化けExcel は BOM なし UTF-8 を Shift_JIS と誤認UTF-8 BOM 付きで保存
Web から取得したデータが文字化けContent-Type と実際のエンコーディングが異なるchardet で検出

BOM(Byte Order Mark)の扱い

UTF-8 BOM 付きファイルを読むと、先頭に不可視文字が残ることがある。

# BOM を自動処理
with open('bom_file.txt', encoding='utf-8-sig') as f:
    content = f.read()

utf-8-sig は BOM を自動的に除去する。書き込み時には BOM を付加する。

バイナリモードで開く

エンコーディングの問題を回避するため、バイナリモードで開いてから自分でデコードする方法もある。

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

# 複数のエンコーディングを試す
for encoding in ['utf-8', 'shift_jis', 'euc_jp', 'cp932']:
    try:
        content = raw.decode(encoding)
        print(f'{encoding} で成功')
        break
    except UnicodeDecodeError:
        continue

安全な読み込み関数

実用的な汎用関数の例を示す。

import chardet

def safe_read(filepath, fallback_encoding='utf-8'):
    with open(filepath, 'rb') as f:
        raw = f.read()
    
    # UTF-8 を最初に試す
    try:
        return raw.decode('utf-8')
    except UnicodeDecodeError:
        pass
    
    # chardet で検出
    detected = chardet.detect(raw)
    if detected['encoding']:
        try:
            return raw.decode(detected['encoding'])
        except (UnicodeDecodeError, LookupError):
            pass
    
    # フォールバック
    return raw.decode(fallback_encoding, errors='replace')