ファイルの読み書きでエンコーディングを正しく扱わないと、文字化けやエラーが発生する。日本語を扱う際は特に注意が必要だ。
エンコーディングとは
エンコーディングは、文字をバイト列に変換する方式だ。同じテキストでもエンコーディングが違えばバイト列が変わる。
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')