はじめに なぜ「BOM付きUTF-8保存」が業務で大事なのか
業務システムだと、こんな場面がよく出てきます。
- 外部システムから「UTF-8(BOM付き)で送ってください」と仕様で指定されている。
- Excel や古いツールが「BOM付きUTF-8 じゃないと正しく開いてくれない」。
- 自分たちが後でエンコーディング判定しやすいように、BOMを付けておきたい。
ここで「UTF-8 で保存すればいいんでしょ?」とだけ考えていると、
「BOM付きになっていない」「逆にBOMが付いて困る」といった事故が起きます。
なので、「C# ではどう書けば BOM付きUTF-8 になるのか」「BOMなしとどう切り替えるのか」を、意識してコントロールできるようにしておくことが大事です。
BOM付きUTF-8の基本を整理する
BOMとは何か
BOM(Byte Order Mark)は、テキストファイルの先頭に付く数バイトの「目印」です。
UTF-8 の場合、先頭に EF BB BF という 3 バイトが付きます。
この 3 バイトがあることで、「このファイルは UTF-8 ですよ」と機械が判定しやすくなります。
逆に、BOMがない UTF-8(いわゆる UTF-8N)は、バイト列だけ見ても UTF-8 なのか Shift_JIS なのか判別しづらいことがあります。
C# の Encoding.UTF8 は「BOM付き前提」のエンコーディング
Encoding.UTF8 プロパティは、UTF8Encoding のインスタンスを返します。
このインスタンスは GetPreamble() を呼ぶと EF BB BF を返す=「BOMを持っている」UTF-8 です。
つまり、「Encoding.UTF8 を指定してテキストを書き込む」と、多くの場合 BOM付きUTF-8 になります。
一番簡単な「BOM付きUTF-8保存」
File.WriteAllText を使うパターン
File.WriteAllText に Encoding.UTF8 を渡すのが、もっともシンプルです。
using System.IO;
using System.Text;
string path = @"C:\data\sample_utf8_bom.txt";
string content = "こんにちは、世界!";
File.WriteAllText(path, content, Encoding.UTF8);
C#ポイントはここです。
- 第3引数に
Encoding.UTF8を渡している。 Encoding.UTF8は BOM を持つ UTF-8 なので、ファイル先頭にEF BB BFが付きます。
これだけで、「BOM付きUTF-8で保存する」要件はほぼ満たせます。
StreamWriter を使うパターン
行単位で書きたい、逐次書き込みたいときは StreamWriter を使います。
using System.IO;
using System.Text;
string path = @"C:\data\log_utf8_bom.txt";
using (var writer = new StreamWriter(path, false, Encoding.UTF8))
{
writer.WriteLine("1行目");
writer.WriteLine("2行目");
}
C#ここでも同じく、Encoding.UTF8 を渡しているので BOM付きUTF-8 になります。
「BOM付き」と「BOMなし」を明示的に切り替える
UTF8Encoding コンストラクタで制御する
UTF8Encoding には、「BOMを出すかどうか」を指定できるコンストラクタがあります。
using System.Text;
// BOM付きUTF-8
var utf8WithBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);
// BOMなしUTF-8
var utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
C#これを File.WriteAllText や StreamWriter に渡せば、BOMの有無を完全にコントロールできます。
例:BOM付きUTF-8で保存するユーティリティ
using System.IO;
using System.Text;
public static class Utf8BomFileUtil
{
private static readonly Encoding Utf8WithBom =
new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);
public static void WriteAllTextWithBom(string path, string content)
{
File.WriteAllText(path, content, Utf8WithBom);
}
}
C#呼び出し側は、こう書くだけです。
Utf8BomFileUtil.WriteAllTextWithBom(
@"C:\data\export.csv",
"ヘッダー1,ヘッダー2\n値1,値2");
C#「BOM付きUTF-8で保存したい」という意図が、メソッド名からもコードからもはっきり伝わるのがポイントです。
実務で使えるユーティリティ例
文字列コレクション(行の列)を BOM付きUTF-8 で保存
CSV やログなど、「行の列」をそのまま書き出したいケースは多いので、IEnumerable<string> を受け取るユーティリティを用意しておくと便利です。
using System.Collections.Generic;
using System.IO;
using System.Text;
public static class Utf8BomFileUtil
{
private static readonly Encoding Utf8WithBom =
new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);
public static void WriteLinesWithBom(string path, IEnumerable<string> lines)
{
using var writer = new StreamWriter(path, false, Utf8WithBom);
foreach (var line in lines)
{
writer.WriteLine(line);
}
}
}
C#使い方の例です。
var lines = new[]
{
"Id,Name,Price",
"1,Apple,120",
"2,Banana,80"
};
Utf8BomFileUtil.WriteLinesWithBom(@"C:\data\items.csv", lines);
C#これで、「Excel で開いても文字化けしない BOM付きUTF-8 の CSV」を安定して出力できます。
既存のファイルを「BOM付きUTF-8」に変換する
「Shift_JIS のファイルを読み込んで、BOM付きUTF-8 で保存し直す」といった変換も、よくある業務パターンです。
using System.IO;
using System.Text;
public static class EncodingConvertUtil
{
public static void ConvertToUtf8Bom(string sourcePath, string destPath, Encoding sourceEncoding)
{
string text = File.ReadAllText(sourcePath, sourceEncoding);
var utf8WithBom = new UTF8Encoding(true);
File.WriteAllText(destPath, text, utf8WithBom);
}
}
C#使い方の例です。
Encoding sjis = Encoding.GetEncoding(932);
EncodingConvertUtil.ConvertToUtf8Bom(
@"C:\data\input_sjis.csv",
@"C:\data\output_utf8_bom.csv",
sjis);
C#よくあるハマりどころと注意点
「デフォルトが BOM付きかどうか」を曖昧にしない
.NET のバージョンや API によって、「デフォルトの UTF-8 が BOM付きかどうか」の挙動が変わってきた歴史があります。
だからこそ、実務では「Encoding.UTF8 を明示する」「UTF8Encoding(true/false) を明示する」といった形で、
コード上に意図をはっきり書くのが安全です。
「何も指定しなかったらたまたま BOM付きだった/なかった」に依存しないようにしましょう。
追記(Append)するときは既存ファイルのBOMに注意
File.AppendAllText や StreamWriter(path, append: true, ...) で追記するとき、
すでにファイルに BOM が付いている場合、先頭の BOM はそのまま残り、
追記部分には当然 BOM は付きません。
これは正常な挙動ですが、
「最初に BOMなしで作ってしまったファイルに、途中から BOM付きUTF-8 で追記する」と、
先頭に BOM がない「中途半端な UTF-8 ファイル」になってしまいます。
「このファイルは必ず BOM付きUTF-8」と決めたいなら、
- 最初に作るときに必ず BOM付きで作る
- 追記も同じエンコーディングで行う
というルールをチームで共有しておくと安心です。
まとめ 「BOM付きUTF-8保存」を自分でコントロールできるようにする
ここまでのポイントを整理すると、こうなります。
- BOM付きUTF-8 は、先頭に
EF BB BFが付いた UTF-8 テキストファイル。 - C# では
Encoding.UTF8やnew UTF8Encoding(true)を使うことで、BOM付きUTF-8 を明示的に出力できる。 File.WriteAllTextやStreamWriterに、そのエンコーディングを渡せば OK。- 「BOM付き」「BOMなし」を切り替えたいときは、
UTF8Encoding(true/false)を使うと分かりやすい。 - デフォルト挙動に頼らず、「なぜ BOM付きになるのか」をコードで表現しておくと、将来のバージョン差異にも強くなる。
ここまで押さえておけば、「なんとなく UTF-8 で書いている」状態から抜け出して、
「仕様どおりに BOM付きUTF-8 を出力できるユーティリティ」を自信を持って書けるようになります。
