テキストファイルの先頭に置かれる謎の3バイトや、ExcelでCSVが文字化けする問題の多くはBOM(Byte Order Mark)が関係します。
本記事では、BOMの正体と役割、特にUTF-8でのBOMあり/なしの違い、UTF-16やUTF-32での重要性、そして実務での注意点やベストプラクティスを初心者向けに丁寧に解説します。
BOMの基礎

BOM(Byte Order Mark)とは
BOM(Byte Order Mark)は、テキストファイルの先頭に付与される特別なバイト列で、主にそのファイルがどの文字エンコーディングで書かれているか、さらに一部のエンコーディングではバイト順序(エンディアン)を表すために使われます。
BOMは目に見える文字ではありませんが、エディタやプログラムが読み取り時に参照して、ファイルを正しく解釈する助けになります。
U+FEFFと先頭マーク
BOMはUnicodeのコードポイントU+FEFFに由来します。
U+FEFFは歴史的に「幅ゼロの非改行スペース(ZERO WIDTH NO-BREAK SPACE)」としても使われましたが、現在は文中での使用は非推奨であり、先頭に現れる場合のみ「BOM」として扱うのが原則です。
文中にU+FEFFが混入すると、目に見えない不可視文字がプログラムや検索の挙動に影響することがあり、デバッグを難しくします。
そのため、本文中の不可視制御にはU+2060(WORD JOINER)やU+200B(ZERO WIDTH SPACE)などが推奨されますが、通常のテキストでは必要最小限にとどめるのが安全です。
エンコーディング検出の役割
エディタやプログラムは、ファイルの先頭にBOMがあれば、そのヒントからエンコーディングを確実に判定できます。
特にUTF-16やUTF-32では、エンディアンの違いが存在するため、BOMがないと解釈に失敗する可能性があります。
一方でUTF-8はバイト順の概念がないため、BOMは必須ではありません。
BOMがない場合、ツールは拡張子やヒューリスティック、システムの既定コードページに依存して推測するため、誤判定による文字化けにつながることがあります。
UTF-8のBOMあり/なしの違い
UTF-8 BOMのバイト列 EF BB BF
UTF-8におけるBOMは3バイトのシグネチャで、16進表記で「EF BB BF」です。
ファイルの先頭がこの3バイトで始まっていれば、一般に「UTF-8 with BOM」と呼ばれます。
BOMがなければ「UTF-8 without BOM」です。
UTF-8ではBOMは機能的に必須ではないため、目的やツールの相性で使い分けます。
先頭3バイトの確認例
LinuxやmacOSでは、次のように先頭バイトを確認できます。
hexdump -C file.txt | head -n 1
# 先頭が ef bb bf なら UTF-8 BOM あり
BOMありの利点と欠点
BOMありの主な利点は、Windows系ツールを中心に「このファイルはUTF-8です」と明示でき、誤判定による文字化けを避けやすい点です。
特にExcelや古いエディタはBOMがないUTF-8をCP932(Shift_JIS系)などと誤認することがよくあります。
一方で欠点も明確です。
UNIX系の多くのツールやスクリプトはBOMをデータの一部として扱うため、次のような不具合を招くことがあります。
ファイル結合時に不要な不可視文字が混入する、先頭行のシェバン(#!)の前にBOMがあると実行エラーになる、JSONや一部の設定ファイルがパースエラーになる、HTTPレスポンスの先頭にBOMが混ざってプロトコルとして不正になる、といった問題です。
BOMなしが推奨される場面
ソースコード、シェルスクリプト、設定ファイル(例: .env、.ini、YAML、JSON)、HTTPや各種プロトコルで送受信するテキスト、テンプレートやインクルードされるサーバーサイドのスクリプト(PHPのinclude対象など)では、UTF-8 without BOMが一般に推奨されます。
これらはBOMがデータとして扱われやすく、思わぬ障害の原因になりやすいからです。
エディタやサーバー環境でUTF-8を明示できるなら、BOMに頼らず設定で統一するのが安全です。
文字化けやパースエラー例
BOMが原因でよく起きる症状を具体的に挙げます。
- ラテン系エンコーディングとして誤解釈された例
先頭のEF BB BFが「」に見えることがあります。ログや画面に「」が出たら、BOM漏れ込みを疑います。 - JSONのパースエラー
一部の実装では先頭BOMを許容せず、次のようなエラーが出ます。
例: Node.jsでJSON.parse
時に「Unexpected token in JSON at position 0」(先頭に不可視のU+FEFFがある)。 - PHPで「Headers already sent」
PHPファイルの先頭にBOMがあると、HTTPヘッダ出力前にすでにバイトが送られたことになり、セッション開始やCookie送出に失敗します。 - シェルスクリプトの実行エラー
先頭のBOMが原因で、bad interpreter: No such file or directory
やenv: node\r: No such file or directory
のようなエラーが発生します。この件は「シェバンとBOMの衝突」で詳しく触れます。
UTF-16・UTF-32とエンディアン
BOMでエンディアン識別
UTF-16とUTF-32は、メモリ上のバイト順序(ビッグエンディアンかリトルエンディアンか)が違っていても同じコードポイントを表現できるため、受け手が正しく復号するには順序の情報が必要です。
BOMはこの順序を明示する役割を担います。
FE FFとFF FEの違い
UnicodeのU+FEFFを数値の並びとして先頭に置いたとき、ビッグエンディアンでは「FE FF」、リトルエンディアンでは「FF FE」という並びになります。
これにより、読み手はファイルがBEかLEかを判断できます。
次の表は、代表的なUnicode変換形式と先頭BOMのバイト列をまとめたものです。
エンコーディング | 先頭のBOMバイト列 |
---|---|
UTF-8 | EF BB BF |
UTF-16BE | FE FF |
UTF-16LE | FF FE |
UTF-32BE | 00 00 FE FF |
UTF-32LE | FF FE 00 00 |
なぜUTF-16/32でBOMが重要か
UTF-8はバイト順の概念がないためBOMは任意ですが、UTF-16やUTF-32ではBOMがないと受け手がエンディアンを誤る可能性があります。
特にファイル受け渡しやAPIレスポンスのように、外部へバイナリとして渡す場面では、BOMがあることで誤解釈を防げます。
Windows内部ではUTF-16LEが多用されますが、外部とやり取りするテキストについてはBOMにより自己記述的にしておくと安全です。
実務の注意点とベストプラクティス
エディタ設定と保存形式
日常的に使うエディタで、保存時のエンコーディングとBOMの有無を明示的に選べるようにしておくと事故が減ります。
Visual Studio Codeなら「ファイル」→「エンコード付きで保存」で「UTF-8」(BOMなし)と「UTF-8 with BOM」を切り替えられます。
設定のfiles.encoding
をutf8
に、BOMが必要な一部ファイルだけ個別に切り替える運用が安全です。
Vimではset fileencoding=utf-8
とともにset nobomb
(BOMなし)またはset bomb
(BOMあり)で制御できます。
Windowsのメモ帳(Notepad)は近年のバージョンで既定がUTF-8(BOMなし)に改善されていますが、保存時の形式は毎回確認するのが確実です。
GitでのBOM管理
GitはBOMを通常のファイル内容として扱います。
つまり、BOMの有無は差分やハッシュに影響します。
チームで方針を決め、リポジトリ全体を「UTF-8 without BOM」で統一するなどのルール化が有効です。
既存のBOM混入を探すには、ターミナルでgit grep $'\ufeff'
のように検索できます。
自動化するなら、pre-commitフックで先頭のU+FEFFを除去するスクリプトを入れる方法があります。
.gitattributesのworking-tree-encoding
を使ってワークツリーとリポジトリの文字コードを変換する高度な設定もありますが、初心者のうちはプロジェクト全体で「UTF-8(BOMなし)」にそろえるのが運用しやすいです。
Excel用CSVはUTF-8 BOM
Windows版Excelは歴史的経緯から、UTF-8 BOMがないCSVをShift_JIS相当として読み込むことがあり、日本語が文字化けします。
実務でExcel取り込みが前提のCSVを出力する場合は、「UTF-8 with BOM」で保存するのがもっとも安全です。
WebアプリやバックエンドからCSVを書き出す際も、Excelユーザー向けにはBOM付きでエクスポートするオプションを用意するとトラブルを減らせます。
シェバンとBOMの衝突
シェルスクリプトやNode.js、Pythonのスクリプトは、ファイル先頭に「#!」で始まるシェバン行を書くのが通例です。
ここで先頭にBOMがあると、OSがシェバンを正しく認識できず、bad interpreter: No such file or directory
のような実行エラーになります。
解決策は、スクリプトをUTF-8(BOMなし)で保存し直すことです。
手元で除去する場合は、LinuxやmacOSなら次で先頭BOMを1回だけ削除できます。
sed -i '1s/^\xEF\xBB\xBF//' script.sh
HTML/JSONでのBOM
HTML5は先頭BOMを許容し、BOMはエンコーディング宣言として最優先で解釈されます。
そのためHTMLに限ればBOMが障害になることは稀ですが、テンプレートエンジンやサーバー側で連結する構成ではBOMが思わぬ位置に現れることがあるため、基本はBOMなしとし、HTTPヘッダや<meta charset="UTF-8">
で明示するのが堅実です。
JSONについては注意が必要です。
RFC 8259は「UTF-8のJSONテキストにBOMを付加してはならない」と規定しており、BOMを拒否する実装も存在します。
現場では、JSONは必ずUTF-8(BOMなし)で保存し、パーサ側で先頭のU+FEFFを特別扱いしない方針にすると相互運用で悩まされにくくなります。
BOMの付加・除去方法
BOMの有無はツールで安全に切り替えられます。
いくつか実用的な方法を紹介します。
- エディタでの切り替え
VS Codeなら「エンコード付きで保存」を使い、「UTF-8」(BOMなし)と「UTF-8 with BOM」を目的に応じて選択します。Vimは:set bomb
または:set nobomb
で制御し、:w
で保存します。 - コマンドラインで除去
Linux/macOSでは、先頭のUTF-8 BOMを削除するには次のワンライナーが便利です。sed -i '1s/^\xEF\xBB\xBF//' file.txt
- PowerShellでの出力
Windows PowerShell 5.1のOut-File -Encoding UTF8
はBOMありで出力します。PowerShell 7以降では既定がUTF-8(BOMなし)になり、必要な場合は-Encoding utf8BOM
を指定します。BOMなしで保存したいときは-Encoding utf8NoBOM
が明確です。
例:Get-Content in.txt -Raw | Out-File out.txt -Encoding utf8NoBOM
- 検出の確認
先頭3バイトがEF BB BFかどうかをhexdump
やxxd
で確認します。Windowsではfc /b file.txt
でも先頭バイトを確認できます。 - ほかのユーティリティ
ICUのuconv --remove-signature
はBOMの除去に便利です。Notepad++などのGUIエディタでも「UTF-8 BOMあり/なし」を明示して変換保存できます。
必要以上にBOMの付加と除去を繰り返すと差分が雑音化しやすいため、プロジェクトで「どのファイルをBOMありにするか」を最初に決め、保存設定を統一しておくのが長期的に効きます。
まとめ
BOM(Byte Order Mark)は、テキストのエンコーディングやバイト順序を示すための先頭マークで、特にUTF-16/UTF-32では不可欠な役割を果たします。
UTF-8におけるBOMは任意ですが、Windows系ツールでは正しい判定に役立つ一方、UNIX系のスクリプトやJSON、設定ファイルでは不具合の原因になりやすいため、基本はUTF-8(BOMなし)で統一し、Excel向けCSVなど必要な場面だけBOMありを採用するのが現実的です。
エディタとGitの設定を整え、BOMの有無をチームでルール化することで、文字化けやパースエラーを未然に防げます。
目的に応じてBOMを正しく使い分け、見えない1文字に振り回されない開発環境を整えていきましょう。