CSVが文字化けする理由|UTF-8とShift_JISの違いとBOMの正体
取引先から届いたCSVをExcelで開いたら、こうなっていた。
ID,蝠�蜩∝錐,譚ア莠ャ驛ス,螢イ荳�
1,譬ェ蠑丈シ夂、セ...
あるいは、こう。
ID,���i��,�����s
1,������...
どちらも「文字化け」ですが、この2つは原因が正反対です。そして化け方を見れば、どちらが起きているのかを見分けられます。
なぜCSVだけ、こんなに文字化けするのか。
結論から言うと、CSVというファイル形式には「このファイルは何の文字コードで書かれている」と書いておく場所が存在しないからです。HTMLにもXMLにもJSONにもあるその仕組みが、CSVにはありません。
文字化けの正体は「同じ数字を別の表で読んだ」だけ
コンピュータはファイルの中身をすべて数字(バイト)で持っています。文字そのものは保存されていません。
たとえば「あ」という文字をUTF-8で保存すると、ファイルの中身はこの3バイトになります。
E3 81 82
この数字を「UTF-8の対応表」で引けば「あ」に戻ります。ところが同じ数字を「Shift_JISの対応表」で引くと、別の文字になります。
E3 81 82 → UTF-8で読む → あ
E3 81 82 → Shift_JISで読む → 縺(+読めない1バイト)
これが文字化けです。ファイルが壊れているわけではなく、書いた側と読む側で参照した表が違っただけ。だから正しい表で開き直せば、元通りに読めます。
実際の化け方(UTF-8のファイルをShift_JISとして読んだ場合)
業務でよく出てくる単語で、実際に起きる変化がこちらです。
| 本来の文字 | UTF-8での中身 | Shift_JISとして読むと |
|---|---|---|
| 商品名 | E5 95 86 E5 93 81 E5 90 8D | 蝠蜩∝錐 |
| 東京都 | E6 9D B1 E4 BA AC E9 83 BD | 譚ア莠ャ驛ス |
| 株式会社 | E6 A0 AA E5 BC 8F E4 BC 9A E7 A4 BE | 譬ェ蠑丈シ夂、セ |
| 日本語 | E6 97 A5 E6 9C AC E8 AA 9E | 譌・譛ャ隱 |
見慣れない漢字と半角カナが混じった、独特の見た目になります。「譌・」「蝠」「驛ス」あたりが出てきたら、まずこのパターンだと思って構いません。
【診断】化け方で原因が特定できる
ここが実務で一番役に立つところです。文字化けには2つの方向があり、見た目がはっきり違います。
| 見た目 | 起きていること | |
|---|---|---|
| パターンA | 蝠蜩∝錐 譚ア莠ャ驛ス (変な漢字+半角カナ) |
中身はUTF-8。 それをShift_JISとして読んでいる |
| パターンB | ���i�� �����s (黒いひし形の連続) |
中身はShift_JIS。 それをUTF-8として読んでいる |
なぜ見た目が違うのか。理由はバイト列の性質にあります。
- パターンA:UTF-8のバイト列は、たまたまShift_JISの漢字としても成立してしまう範囲に収まることが多い。だから「読めるけれど意味不明な漢字」になる
- パターンB:Shift_JISのバイト列は、UTF-8のルール上ありえない並びになる。読み手は「解釈不能」と判断し、置換文字「�」(U+FFFD)に置き換える
つまり「�」が並んでいたら、元ファイルはShift_JIS。変な漢字が並んでいたら、元ファイルはUTF-8。開き直すべき文字コードが即座に分かります。
根本原因:CSVには文字コードを書く場所がない
ではなぜ、読む側は文字コードを間違えるのか。答えは単純で、ファイルのどこにも書いていないからです。
他の形式と比べると、CSVの特殊さがはっきりします。
| 形式 | 文字コードの伝え方 |
|---|---|
| HTML | ファイル内に <meta charset="UTF-8"> と書ける |
| XML | 1行目に <?xml version="1.0" encoding="UTF-8"?> と書ける |
| JSON | 仕様でUTF-8が基本と決まっている |
| CSV | ファイル内に書く場所がない |
CSVの仕様を定めた文書(RFC 4180)を読むと、文字コードの扱いはこう書かれています。
CSVの一般的な用法はUS-ASCIIだが、IANAが定義する他の文字集合を 「charset」パラメータと組み合わせて 使ってもよい
重要なのは「組み合わせて」の部分です。charsetはファイルの中ではなく、ファイルの外側——たとえばWebサーバーが送信時に付ける Content-Type: text/csv; charset=UTF-8 というヘッダーで伝える設計になっています。
ここに落とし穴があります。
CSVをメールに添付した瞬間、USBメモリにコピーした瞬間、文字コードの情報は消える。
残るのは中身のバイト列だけ。受け取った側のExcelは、手がかりのない状態で「たぶんこれだろう」と推測するしかありません。その推測が外れると文字化けします。API経由のやり取りでヘッダーが付く場合と違い、ファイルの手渡しでは情報が失われるわけです。
UTF-8とShift_JISの違い
そもそもこの2つは何が違うのか。実務に効く点だけ整理します。
| UTF-8 | Shift_JIS | |
|---|---|---|
| 読み方 | ユーティーエフエイト | シフトジス |
| 扱える文字 | 世界中のほぼ全文字 | 日本語と英数字のみ |
| 漢字1文字の容量 | 3バイト | 2バイト |
| 絵文字 | 使える | 使えない |
| 現在の位置づけ | Web・システムの標準 | 古い日本製システムで残存 |
Shift_JISは日本語専用に作られた古い規格です。容量が小さくて済む利点はありましたが、致命的な弱点があります。
Shift_JISに変換すると、文字が消えることがある
Shift_JISは日本語しか表現できません。表現できない文字を無理に変換すると、その文字は失われます。
| 文字 | UTF-8 | Shift_JIS |
|---|---|---|
| あ | 3バイト | 2バイト |
| 𠮷(つちよし) | 4バイト | 変換できない |
| 😀(絵文字) | 4バイト | 変換できない |
| ハングル・タイ文字 | 可 | 変換できない |
顧客名簿の「𠮷田」さんが「?田」や「吉田」に変わってしまう——これがShift_JIS変換で起きる事故です。文字化けは開き直せば直りますが、変換して保存した場合は元に戻りません。電子帳簿保存法の対象データのように原本性が問われるものでは、特に注意が必要です。
BOMの正体——本来UTF-8には不要なもの
CSVの文字化け対策を調べると、必ず「BOM付きUTF-8で保存する」という話が出てきます。このBOMが何なのか、実はかなりねじれた事情があります。
BOM(Byte Order Mark)はファイルの先頭に置かれる3バイトの目印です。
EF BB BF
本来の役割は「バイト順(Byte Order)の指定」でした。UTF-16のように2バイト単位で文字を扱う方式では、2バイトをどちらの順で並べるかが2通りあり、その区別が必要だったのです。
ところがUTF-8は1バイト単位で処理する方式なので、そもそもバイト順という概念がありません。UTF-8の仕様書(RFC 3629)にもこう明記されています。
UTF-8は単一オクテットの符号化単位を持つため、この(バイト順を認識する)機能は無意味である
それどころか、同じ仕様書はBOMの使用を積極的に制限すべきだと述べています。
常にUTF-8であることが定められている要素については、プロトコルはBOMの使用を禁止すべきである(SHOULD forbid)。その場合、目印としての機能はまったく無用だからである
Unicodeを策定している団体自身も、UTF-8でのBOMは「許容はするが推奨しない」という立場です。
ではなぜ、BOMを付けるのか
ここで話が一周します。RFC 3629がBOMを禁止すべきだとする理由は「文字コードを識別する仕組みが他にあるなら不要だから」でした。
裏を返せば——識別する仕組みがない場合には、BOMに頼るしかないということです。
そしてCSVは、まさにその「仕組みがない」形式です。Excelに伝える手段が先頭の3バイトしか残っていないため、規格上は望ましくないものが実務では必需品になっています。
BOM付きUTF-8は、CSVに文字コード宣言がないことへの苦肉の策。
BOMが引き起こす別のトラブル
規格がBOMを嫌う理由は、実害があるからです。BOMは目印であると同時にファイルの中身の一部でもあるため、それを知らないプログラムはデータとして読んでしまいます。
先頭の列名がおかしくなる
最も多いトラブルがこれです。BOM付きCSVをプログラムで読むと、1列目の見出しが変わります。
# 期待している列名
'ID'
# 実際に読み込まれる列名
'ID'
画面上は「ID」に見えるのに、プログラムからは別の名前として扱われる。そのため「ID列が見つからない」というエラーになります。目に見えない文字が原因なので、非常に気づきにくいトラブルです。同じ「見えない文字」によるトラブルは、改行コード(CRLFとLF)の違いでも起こります。
対処法
Pythonなら、読み込み時のエンコーディング指定を utf-8 ではなく utf-8-sig にすればBOMを自動で取り除いてくれます。
# BOMが列名に混入する
open('data.csv', encoding='utf-8')
# BOMがあれば自動的に除去される
open('data.csv', encoding='utf-8-sig')
設定ファイルやスクリプトの先頭にBOMが入って動かなくなる、というトラブルも同じ原因です。YAMLのような設定ファイル形式でも起こりえます。
結局どうすればいいか(ケース別)
用途によって正解が変わります。ここを混同すると対策が空振りします。
| 用途 | 推奨 | 理由 |
|---|---|---|
| Excelで開いてもらう | BOM付きUTF-8 | Excelが文字コードを判別できる |
| システム間の連携 | BOMなしUTF-8 | BOMがデータに混入する事故を防ぐ |
| 相手が古いシステム | 指定に従う | Shift_JIS指定なら従うほかない |
渡す相手が「人」か「システム」かで分ける、と覚えると迷いません。
Excelでの保存方法
Excelの「名前を付けて保存」で、ファイルの種類にこの2つが並んでいます。
- CSV UTF-8(コンマ区切り) … BOM付きUTF-8で保存される
- CSV(コンマ区切り) … 日本語環境ではShift_JISで保存される
相手を選ばない安全側は前者です。ただし前述のとおり、システムに取り込ませる用途では逆にBOMが問題になります。
届いたCSVが化けていたときの手順
- 化け方を見る(変な漢字ならUTF-8、「�」ならShift_JIS)
- Excelで直接開かず、メモ帳などで開いて文字コードを確認する
- Excelの「データ」タブ →「テキストまたはCSVから」を使い、文字コードを指定して読み込む
ダブルクリックで開くとExcelが勝手に推測してしまいます。「データ」タブからの読み込みなら文字コードを明示的に選べるため、確実です。
よくある質問
Q. 文字化けしたファイルは壊れているのですか?
いいえ。中身のバイト列は無傷で、読み方を間違えているだけです。正しい文字コードで開き直せば元通りになります。ただし化けた状態で上書き保存すると本当に壊れます。まず保存せず、開き方を変えてください。
Q. UTF-8とUTF-8 BOM付きは別の文字コードですか?
文字コードとしては同じUTF-8です。違いは先頭に3バイトの目印があるかどうかだけです。
Q. なぜExcelは勝手にShift_JISだと判断するのですか?
CSVに文字コードの情報がないため、推測するしかないからです。日本語版Excelは長らくShift_JISを既定としてきた経緯があり、その挙動が残っています。BOMがあればUTF-8だと判断できます。
Q. Shift_JISはもう使わないほうがいいですか?
新しく作るデータではUTF-8を選ぶべきです。ただし取引先の既存システムがShift_JIS指定なら従うしかありません。その場合は表現できない文字が失われる点を前提に、氏名や住所のデータを扱う際は注意が必要です。
Q. 「文字コード」と「フォント」は違うものですか?
まったく別物です。文字コードは「どの文字か」を決める数字の対応表、フォントは「どう見えるか」を決めるデザインです。フォントを変えても文字化けは直りません。
参考にした主な調査・資料
- RFC 4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files(IETF)— CSVの仕様。文字コードはcharsetパラメータで指定する旨の記述
- RFC 3629: UTF-8, a transformation format of ISO 10646(IETF)— UTF-8の仕様。第6章にBOMの扱いと使用制限の記述
- FAQ – UTF-8, UTF-16, UTF-32 & BOM(Unicode Consortium)— UTF-8でのBOMの位置づけ
まとめ
CSVの文字化けは、CSVという形式の構造的な欠陥から生まれています。
- 文字化けは同じバイト列を別の表で読んだ結果。ファイルは壊れていない
- 変な漢字なら元はUTF-8、「�」なら元はShift_JIS。化け方で原因が分かる
- 原因はCSVに文字コードを書く場所がないこと。ファイルで渡すと情報が消える
- BOMは規格上は非推奨だが、CSVには他に手段がないため実務では必要悪
- 渡す相手が人(Excel)ならBOM付き、システムならBOMなし
- Shift_JISへの変換は不可逆。表現できない文字は失われる
文字化けを見たときに慌てて保存し直すのではなく、まず化け方を観察する。それだけで対処の精度が大きく変わります。