3秒でわかる
書いた側と読む側で文字コードの解釈が食い違い、文字が読めない記号に化ける現象。原因はデータではなく指定の不一致にあります。
もう少し詳しく
どういうものか
文字化けは、文字を保存したときのエンコーディングと、読み出すときに想定したエンコーディングが違うために起きる表示の崩れです。文字はメモリ上ではただのバイト列で、どの並びをどの文字に対応させるかは規則が決めています。UTF-8 で書いた日本語は1文字あたり3バイト前後になりますが、これを Shift_JIS の規則で読むと、まったく別の文字の並びとして解釈されます。
「あ」を UTF-8 で保存 -> E3 81 82
それを Shift_JIS で読む -> 縺・(意味の無い並び)データ自体は壊れていません。読み方だけが間違っています。
なぜ必要か
原因の見当が付くかどうかで、対処にかかる時間がまったく違います。文字化けを見て「データが壊れた」と考えると、元ファイルを作り直そうとして時間を溶かします。実際に確認すべきは、保存時の指定、転送時の宣言、表示時の指定という3か所のうちどこがずれているかです。
具体例
Pythonでは、読み書きのたびにエンコーディングを明示するのが最も確実です。
# 明示しないと環境依存になる。Windows では cp932 が既定になることがある
with open("data.csv", encoding="utf-8") as f:
text = f.read()
# 壊れた文字を落として先へ進めたいとき
with open("legacy.csv", encoding="shift_jis", errors="replace") as f:
text = f.read()
# バイト列がどの規則で読めるかを試す
raw = b"\xe3\x81\x82"
print(raw.decode("utf-8")) # あ<meta charset="utf-8">つまずきやすいところ
Excelが絡む場面が最も多い相談です。UTF-8 のCSVをExcelで開くと化けることがあり、これはExcelが既定でShift_JIS系として読むためです。先頭にBOMを付けた UTF-8 で書き出すと正しく開きます。一方、そのBOM付きファイルをプログラムで読むと、1列目の見出しの先頭に見えない文字が混ざり、列名の一致に失敗します。読む側は encoding="utf-8-sig" を指定して取り除きます。
データベースでは、接続時の文字コード指定を忘れると、テーブルが UTF-8 でも通信の途中で化けます。MySQLで utf8 を指定していた古い設定は3バイトまでしか扱えず、絵文字や一部の漢字が保存できません。utf8mb4 を使います。
一度化けた状態で保存し直すと、元の情報が失われて復元できなくなります。化けを見つけたら、上書き保存する前に原因の指定を直します。
覚え方
文字コードは、同じ数字の並びを読む辞書だと考えます。辞書を取り違えれば別の言葉になりますが、書かれた数字は変わっていません。直すのは辞書の選び方です。