はじめに
私はencodingを雰囲気でやっている。
日頃使っているデータには、日本語、半角カタカナ、漢字、数字、半角英語が混在しているため、基本的にはcp932、shift-jis、UTF-8のどれかを使えばなんとかなると思っている。
そんなふんわりした理解であるため、毎回下記のようなコードを全部書いて総当たりでやってみて、たまたま良い感じに生成できていたファイルを使用する、というアホの運用を何年も続けている。
そして、「何故UTF-8-sigではできたのにUTF-8では文字化けするのか」等を調べずに後続処理に進む。それゆえ一生雰囲気しかわからない。
# df.to_csv('C:/Users/xxxx/Desktop/my_python/df_CP932.csv', encoding='cp932') #ダメ
# df.to_csv('C:/Users/xxxx/Desktop/my_python/df_SHIFT.csv', encoding='shift-jis') #ダメ
# df.to_csv('C:/Users/xxxx/Desktop/my_python/df_none_UTF-8.csv', encoding='UTF-8') #ダメ
#df.to_csv('C:/Users/xxxx/Desktop/my_python/df_none.csv') #ダメ
df.to_csv('C:/Users/xxxx/Desktop/my_python/df_none_UTF-8-sig.csv', encoding='UTF-8-sig') #できた!
pd.read_csv()での読込みの際もテキトーに下記のようなコードを実行する。ダメだったらencoding='utf-8'に書き換えて、できたらそのまま後続処理に進む。
# df = pd.read_csv('C:/Users/xxxx/Desktop/my_python/df.csv', encoding='UTF-8').astype(str) # 文字化けした
df = pd.read_csv('C:/Users/xxxx/Desktop/my_python/df.csv',  encoding='cp932').astype(str) #できた!
このような非効率すぎる処理から卒業したく、encodingについて整理してみることにした。
そもそもencodingとは
encodingとは、文字や記号をコンピュータが理解できる形式に変換するための規則のこと。異なるencoding方式が存在し、それぞれが異なる文字セットや記号をサポートしている。
日本語対応のencoding方式
①cp932、shift-jis:
日本語のWindows環境でよく使われるencoding方式。JIS X 0208に基づいた漢字やカタカナ、ひらがななどを含む文字セットをサポートしている。
(確かに筆者はWindows環境を使用しているため、pd.read_csv()ではcp932をファーストチョイスしている。)
②UTF-8:
Unicodeを使って世界中のほぼすべての文字を表現できるencoding方式。マルチバイト文字セットをサポートしており、国際的なテキストデータの交換に適している。
③UTF-8-sig:
UTF-8と同じだが、ファイルの先頭にバイトオーダーマーク(BOM:Unicodeで書かれたテキストファイル の先頭に付ける「このファイルはUnicodeで書かれているッ!WindowsShift-JISではないッ!」と明確に示す目印データ)を含む。BOM不要の環境では問題を引き起こすことがある。
encodingで詰む理由
encodingの問題が発生する一般的な理由は、使用しているencoding方式がテキストデータに含まれる文字セットを正しくサポートしていない場合である。
例えば、cp932でencodeされたファイルをUTF-8で読み込もうとすると、文字化けが発生する場合がある。
読み込むファイルのencodingが不明な場合は、いくつかのencodingを試してみることが有効な方法である。総当たりでやること自体はそこまで悪いことではないらしい。
事故事例集
①read_csv()とto_csv()でencodingを揃えているのに文字化けすることがあるのはなぜか?
イメージ:
df = pd.read_csv('df.csv',  encoding='cp932')
df.to_csv('df.csv', encoding='cp932')
原因:
- 元ファイルの文字が、
cp932で表現できない文字である - 元ファイルが
cp932以外で保存されている 
cp932はWindowsで全てのUnicode文字をカバーしているわけではない。そのため、cp932で表現できない特殊な文字や絵文字などが含まれていると文字化けが発生しうる。
対策:
utf-8かutf-8-sigで保存する。
②read_csv()とto_csv()でencodingを変えたらどうなるのか
パターン1:cp932で読み込んだものをutf-8で保存して文字化けが発生する場合
イメージ:
df = pd.read_csv('df.csv',  encoding='cp932')
df.to_csv('df.csv', encoding=`utf-8`)
原因:
- 元のファイルに含まれている文字が
cp932で表現できない文字である場合、読み込み時には問題なくても、保存時に文字化けが発生することがある。 - データフレーム内で処理を行った結果、
cp932で表現できない文字が含まれるようになった可能性がある。 
対策:
utf-8-sigで保存する。
パターン2:utf-8で読み込んだものをcp932で保存して文字化けが発生する場合
イメージ:
df = pd.read_csv('df.csv',  encoding=`utf-8`)
df.to_csv('df.csv', encoding='cp932')
原因:
- 
UTF-8で表現できるがcp932で表現できない文字が含まれていると、保存時に文字化けが発生する可能性がある。 - 
cp932で読み込んだ後にUTF-8で保存する場合、通常は文字化けは発生しないが、UTF-8でのみ表現可能な特殊文字が含まれている場合はそれらの文字が正しく保存される。一般的に、UTF-8は国際的に広く使用されており、多くの文字をカバーできるため、UTF-8を使用することが推奨される。 
対策:
utf-8かutf-8-sigで保存する。
③UTF-8で保存すると文字化けするのに、UTF-8-sigなら文字化けせず上手くいくことがあるのは何?
イメージ:
# df.to_csv('C:/Users/xxxx/Desktop/my_python/df_none_UTF-8.csv', encoding='UTF-8') #ダメ
df.to_csv('C:/Users/xxxx/Desktop/my_python/df_UTF-8-sig.csv', encoding='UTF-8-sig') #できた!
原因:
to_csv()でUTF-8で保存した際に文字化けすることがあるのは、ファイル先頭にBOMがないため、一部のテキストエディタやプログラムが正しくエンコーディングを認識できないことがあるため。(特にWindowsのメモ帳など。)
対策:
utf-8-sigで保存する。
これにより、BOM付きのUTF-8ファイルとして保存され、Windows環境でも文字化けせずに正しく扱うことができる。また、他のプラットフォームやプログラムでもUTF-8として広く互換性があるため、一般的にはこの方法が推奨される。
まとめ
| Encoding | 説明 | 推奨度 | 使用状況 | 
|---|---|---|---|
| cp932 | Windows固有の日本語エンコーディング。Shift-JISの拡張版。 | 中 | Windows環境での日本語データの読み書きに使用。一部の特殊文字で問題が発生することがある。Windows環境で日本語のみを扱い、特に外部システムとの互換性に問題がない場合は 'cp932' を使用する。 | 
| shift-jis | 日本語のエンコーディング標準の一つ。 | 中 | 古いシステムやデータで使用されることがあるが、cp932よりも対応文字が少ない。古いシステムやデータの互換性を保つ必要がある場合は 'shift-jis' を使用する。 | 
| UTF-8 | Unicodeを利用した汎用的なエンコーディング。世界中のほぼ全ての文字に対応。 | 高 | 国際的な標準であり、多言語データのやり取りに適している。 | 
| UTF-8-sig | UTF-8にBOMを付加した形式。 | 中 | ExcelでUTF-8のCSVを開く際に文字化けを防ぐために使用。UTF-8ファイルとして保存され、Windows環境でも文字化けせずに正しく扱うことができる。他のプラットフォームやプログラムでもUTF-8として広く互換性がある。 | 
感想
あくまで私個人の業務としては、csv保存はutf-8-sigを指定しておけば大体どうにかなることがわかって良かった。
こんなのもっとはやく調べておけば良かった。
