BOM(バイトオーダーマーク)
Byte Order Mark
概要(サマリー)
BOM(バイトオーダーマーク)とは、テキストファイルの先頭に置かれる、文字コードの種類やバイトの並び順を示す数バイトの目印である。
役割は「このファイルはこの文字コードで書かれています」という宣言に近い。ファイルを開くソフトはこの数バイトを見て、どの文字コードとして読めばよいかを判断できる。
やっかいなのは、この目印が画面には表示されないことである。そのため「見た目は正常なのに、プログラムで読むと1行目だけおかしい」「Excelでは開けるのにシステムに取り込めない」といった、原因の分かりにくいトラブルを引き起こす。日本語のCSVを扱うと、ほぼ必ず一度は遭遇する。
詳細解説
BOMとは何か(見えない3バイト)
BOMの正体は、Unicodeのコードポイント U+FEFF である。この文字は「ゼロ幅ノーブレークスペース」と呼ばれ、幅を持たないため画面には何も表示されない。
これをファイル先頭に置くと、符号化方式によって次のようなバイト列になる。
- UTF-8:
EF BB BF(3バイト) - UTF-16 リトルエンディアン:
FF FE(2バイト) - UTF-16 ビッグエンディアン:
FE FF(2バイト)
ソフトはこの先頭数バイトを見るだけで、文字コードを推測できる。テキストエディタで「UTF-8」と「UTF-8 BOM付き」が別項目になっているのは、この3バイトを付けるかどうかの違いである。
なぜBOMが生まれたのか(バイト順の問題)
そもそもBOMは、UTF-16のために作られた仕組みである。
UTF-16では1文字を2バイトで表すが、その2バイトをどちらの順番で並べるかは、コンピュータの設計によって異なる。先に大きい桁を置く方式をビッグエンディアン、先に小さい桁を置く方式をリトルエンディアンと呼ぶ。
順番を取り違えると、文字はまったく別のものになってしまう。そこで「先頭に U+FEFF を置き、それがどう並んで見えるかで判断させる」という発想が生まれた。これがバイトオーダーマーク(バイト順の目印)という名前の由来である。
一方UTF-8は1バイト単位で処理するため、本来バイト順の問題は存在しない。それでもUTF-8にBOMを付ける習慣が生まれたのは、バイト順を示すためではなく、「このファイルはUTF-8である」と宣言する目印として流用されたからである。
ExcelとCSVをめぐる有名な問題
現場で最もBOMが問題になるのが、日本語CSVをExcelで開く場面である。
Windows版のExcelは、CSVをダブルクリックで開いたとき、BOMがないと文字コードを環境依存の方式(従来はShift_JIS系)だと推測する。そのため、UTF-8のBOMなしCSVを開くと日本語が文字化けする。
逆にBOMを付けておけば、ExcelはUTF-8だと正しく判断し、文字化けせずに開ける。つまりExcel利用者向けのCSVでは、BOMは付けたほうが親切である。
しかし、そのBOM付きCSVをプログラムで読むと、今度は別の問題が起きる。
// BOM付きUTF-8のCSVをそのまま読むと、
// 1列目のヘッダーに見えない3バイトが混入する
const header = lines[0].split(",")[0];
console.log(header); // 見た目は "名前"
console.log(header === "名前"); // false ← 一致しない
ヘッダー名で列を判定している処理は、これで静かに壊れる。エラーにならず「なぜか1列目だけ取得できない」という形で現れるため、原因にたどり着きにくい。
プログラム側でBOMを取り除く
読み込み側で対処する場合は、先頭の U+FEFF を除去する。
JavaScriptでは次のように書ける。
// 先頭にBOMがあれば取り除く
function stripBom(text) {
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
}
Pythonでは、読み込み時の符号化方式に utf-8-sig を指定すると自動的に処理される。
# utf-8-sig はBOMがあれば取り除き、なければそのまま読む
with open("data.csv", encoding="utf-8-sig") as f:
text = f.read()
utf-8 ではなく utf-8-sig を使うのが定石である。BOMの有無が混在するファイルを扱う場合でも安全に動く。
BOMは付けるべきか、付けないべきか
正解は用途によって変わる。判断の目安は次のとおりである。
ソースコードにBOMが付くと、言語や環境によっては構文エラーになったり、PHPで「Headers already sent」の原因になったり、シェルスクリプトの1行目の指定が壊れたりする。開発用のファイルは原則BOMなしと覚えておけばよい。
すでに文字化けしてしまったファイルを受け取った場合は、元の文字コードを推定して読み直すことで復元できることがある。
AIコーディングとの関係
AIにCSVの入出力処理を書かせるとき、BOMは明示的に伝えないと考慮されないことが多い。「動くはずのコードなのに1列目だけ取れない」という相談の多くはこれが原因である。
出力側は、次のように用途を伝えると適切な実装になりやすい。
日本語のCSVをダウンロードさせる処理を書いてください。
利用者がWindows版Excelで開く前提なので、UTF-8にBOMを付けてください。
読み込み側は、逆にBOMを許容するよう指示する。
アップロードされたCSVを読み込む処理を書いてください。
BOM付きUTF-8とBOMなしUTF-8のどちらでも、
1列目のヘッダー名が正しく取得できるようにしてください。
AIが生成したコードをテストするときは、BOM付きとBOMなしの両方のファイルで確認することが重要である。片方だけのテストでは、この不具合は表面化しない。
よくある勘違い
BOMが付いていても見た目は変わらないから無害?
無害ではない。画面に表示されないだけで、実データとしては3バイト存在する。文字列比較、ヘッダー名の判定、JSONのパースなどで静かに失敗する原因になり、しかもエラーメッセージが出ないため発見が遅れやすい。
UTF-8には必ずBOMを付けるべき?
そうではない。UTF-8はバイト順の問題がないため、BOMは本来不要である。Excel対策として付ける場面はあるが、ソースコードや設定ファイルに付けると不具合の原因になる。用途に応じて使い分けるものである。
BOMを付ければ文字化けは必ず防げる?
防げるとは限らない。BOMはあくまで目印であり、読み込む側がそれを解釈してくれる場合にだけ有効である。BOMを無視するプログラムもあるし、そもそもファイルの中身が別の文字コードで書かれていれば、BOMがあっても正しくは読めない。
BOMはブラウザのBOM(Browser Object Model)と同じ?
まったく別物である。この項目で扱うBOMはByte Order Markであり、文字コードの目印である。JavaScriptで window や document を扱う際に出てくるBrowser Object Modelとは無関係なので、文脈で読み分ける必要がある。
まとめ
- BOMは、ファイル先頭に置かれ文字コードやバイト順を示す、画面に表示されない数バイトの目印である
- UTF-8のBOMは
EF BB BFの3バイトで、本来はバイト順ではなく「UTF-8である」という宣言として使われる - Windows版ExcelでCSVを開く場合、BOMがないと日本語が文字化けする
- 一方でプログラムから読むとヘッダー名に混入し、エラーにならないまま処理が壊れることがある
- Excel向けCSVには付け、ソースコードや設定ファイルには付けない、という使い分けが基本である
情報ソース
- Unicode: UTF-8, UTF-16, UTF-32 & BOM FAQ
- Python ドキュメント: codecs — エンコーディングとUnicode
- Microsoft サポート: テキスト (.txt または .csv) ファイルのインポートまたはエクスポート
より詳しくAIに聞いてみよう
- BOM付きUTF-8とBOMなしUTF-8の違いを、実際のバイト列を示しながら説明してください。
- 日本語CSVをExcelで文字化けさせずに開かせる方法を、サーバー側の実装込みで教えてください。
- BOMが原因で起こる不具合の代表例と、その見分け方を教えてください。
- アップロードされたCSVがBOM付きかどうかを判定する処理を書いてください。
- ソースコードにBOMが混入した場合に起こる問題を、言語ごとに整理してください。