C# Tips | ファイル・ディレクトリ操作:BOM付きUTF-8保存

C sharp C#
スポンサーリンク

はじめに なぜ「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.WriteAllTextEncoding.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.WriteAllTextStreamWriter に渡せば、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.AppendAllTextStreamWriter(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.UTF8new UTF8Encoding(true) を使うことで、BOM付きUTF-8 を明示的に出力できる。
  • File.WriteAllTextStreamWriter に、そのエンコーディングを渡せば OK。
  • 「BOM付き」「BOMなし」を切り替えたいときは、UTF8Encoding(true/false) を使うと分かりやすい。
  • デフォルト挙動に頼らず、「なぜ BOM付きになるのか」をコードで表現しておくと、将来のバージョン差異にも強くなる。

ここまで押さえておけば、「なんとなく UTF-8 で書いている」状態から抜け出して、
「仕様どおりに BOM付きUTF-8 を出力できるユーティリティ」を自信を持って書けるようになります。

タイトルとURLをコピーしました