参考:https://uxmilk.jp/48923
参考:https://alaki.co.jp/blog/?p=1236
参考:https://alaki.co.jp/blog/?p=1260

BOMとは

文字コードのUnicodeにはBOMなしとBOMありの区別があり、BOMはUnicodeで符号化したテキストの先頭に付与される数バイトのデータのことである。
BOMによってそのテキストがUnicodeのデータであることや、どの種類の符号化(UTF-8、UTF-16など)を採用しているのかを判別している。

BOMはExcelなどのアプリケーションで活用されているが、プログラムのソースコードなどはBOMを使わないことが多い(プログラムがBOMを処理できないことがあるため)。

BOMなしとBOMありのテキストの違いはその名の通り、BOMがテキストの先頭にあるかどうかのみの違いであるため、BOMがプログラム上で誤動作した場合にBOMが原因であるとは気づきにくい。
最近のエディタはBOMのありなしが分からないものが多く、またBOMの設定がないものもあり、BOMありでもBOMなしとして認識することがある。
そのためBOMありで保存したファイルで文字化けをしていても、BOMの設定がないエディタでは「UTF-8」としか表示されないので「BOM」が原因であることに気づきにくいなんてことがある。
UTF-8で文字化けしている場合、再度UTF-8で保存しなおせばBOMが消え、文字化けがなくなることがある。

UTF-8ではBOMなしを示す際に、「UTF-8N」と書かれることがある。

Windowsのメモ帳では文字コードをUTF-8にすると必ずBOMありになるため、WordPressなんかで<0xEF 0xBB 0xBF>のような記述があった場合は、それをコピペしたことが原因である。

プログラムの実装で注意すること

BOMで悩むのは殆どの場合、CSVであるため、その前提で話をすすめる。
上述のようにExcelはBOMが必要である。
なぜならExcelの標準文字コードはShift-JISのため、UTF-8Nの場合、Shift-JISと解釈され文字化けしてしまうからだ。


余談だがPHP4などの時代はEUC-JP、メールはISO-2022-JP(JISコード)だそう。

創世記に活躍した過去の人「Shift-JIS」
〜中略〜
特殊文字(①㈱など)が確実に文字化けをすることでも有名。
また、ガラケーの基本文字コードでもあったのでプログラマーとして何度、苦汁を飲まされたことか。。。

〜中略〜

その昔まだ「PHP4がでたー!!」とか騒いでいた時に使っていたのは「EUC-JP」でした。
プログラムは「EUC-JP」、HTMLは「Shift-JIS」という今考えるとなんとも頭の悪い組み合わせがデフォルトだった時代がありました。
プログラムを扱う文字コードとしては楽な文字コードでしたが、常に文字コード変換を使わないいけない面倒くささに辟易していたのもいい思い出です。

〜中略〜

その当時は今は亡き「Outlook Express」か「Outlook」が主流で、プログラムからメールを送信するときはこの「ISO-2022-JP」にして送信しないと文字化けがするという素敵仕様だったのでお世話になりました。

https://alaki.co.jp/blog/?p=1236

UTF-8はあらゆる文字を文字化けなしで表現でき、また文字コード変換がないため主流になった。
昨今はほとんどのプログラムがUTF-8で実装されているため、そのままechoでCSVを出力するとBOMなしのUTF-8となって出力される。

そのため、Excelで操作することがを考慮して行頭にBOMを付ける必要がある。

$fp = fopen('php://output', 'w');

// UTF-8の場合
fwrite($fp, pack('C*',0xEF,0xBB,0xBF));
// UTF-16(LE)の場合
fwrite($fp, pack('C*',0xFF,0xFE));

上記コードのようにエンコードのよってBOMは異なる。
また「UTF-16(LE)」と名前の似ている「UTF-16LE」というのもあり、「UTF-16LE」はBOMをつけることができないため注意する。
ただし、PHPの関数「mb_convert_encoding」で「UTF-16(LE)」は「UTF-16LE」と書かなければいけないため注意する。