Pythonと他言語、BOM付きCSVの扱い方を解説します

みなさん、こんにちは。

CSVファイルを扱う際、文字化けに悩まされた経験はありませんか?特に、日本語のデータを含むファイルをWindowsのExcelで開くと、文字がぐちゃぐちゃになってしまうことがありますよね。

この文字化け問題を解決する1つの方法が、BOM(バイトオーダーマーク)付きのUTF-8でCSVファイルを保存することです。以前のブログ記事でも、この方法を詳しく解説しました。

Pythonではencoding="utf-8-sig"と指定するだけでBOM付きCSVを簡単に扱えます。しかし、他のプログラミング言語では少し事情が異なります。

今回は、BOM付きCSVを扱うための各言語ごとの違いと実装方法を、コード例を交えてまとめてみました。

 


 

BOMってそもそも何?

 

BOM(バイトオーダーマーク)とは、テキストファイルの先頭に付く、ファイルの種類やエンコーディングを示す特別なマークのことです。UTF-8ではEF BB BFという3バイトのデータで表現されます。

多くのエディタやプログラムはBOMを気にせず処理しますが、Excelや一部の古いシステムではこのBOMを判別して、エンコーディングを正しく認識する仕組みになっています。

そのため、日本語を含むCSVをExcelユーザーと共有する際には、BOMを付けてあげるのが親切なのです。

 


 

言語別のBOM付きCSVの扱い方

 

Python

Pythonは、BOM付きUTF-8の扱いが最も簡単な言語の1つです。utf-8-sigという専用のエンコーディングを指定するだけで、読み書きの両方を自動的に処理してくれます。

読み込み(input.csv)
import csv

# BOM付きUTF-8として自動的に認識して読み込む
with open("input.csv", "r", encoding="utf-8-sig") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
書き込み(output.csv)
import csv

data = [["名前", "年齢", "所属"], ["山田太郎", 30, "営業部"]]

# BOMを自動的に付与して書き込む
with open("output.csv", "w", encoding="utf-8-sig", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(data)

utf-8-sigを使うだけでOKという手軽さがいいですね!

 

JavaScript (Node.js)

JavaScriptでは、BOMは単なる「隠れた文字」として扱われます。そのため、読み込む際にBOMを自分で取り除く処理が必要ですし、書き込む際も自分でBOMを先頭に追加する必要があります。

読み込み(input.csv)
const fs = require('fs');

let data = fs.readFileSync("input.csv", "utf8");

// BOM(\uFEFF)をチェックして除去
if (data.charCodeAt(0) === 0xFEFF) {
    data = data.slice(1);
}

console.log(data);
書き込み(output.csv)
const fs = require('fs');

const rows = [
    ["名前", "年齢", "所属"],
    ["山田太郎", "30", "営業部"]
];

let csv = rows.map(r => r.join(",")).join("\n");

// BOM(\uFEFF)を文字列の先頭に付与
csv = "\uFEFF" + csv;

fs.writeFileSync("output.csv", csv, "utf8");

BOMはUnicodeでは\uFEFFという文字として表現されるため、これを文字列として扱います。手動での処理が必須になります。

 

Java

JavaもJavaScriptと同様に、BOMを自動で処理しません。読み込み・書き込みともに、自分でBOMの有無をチェックしたり、追加したりする必要があります。

読み込み(input.csv)
import java.io.*;

public class ReadCSV {
    public static void main(String[] args) throws Exception {
        try (BufferedReader br = new BufferedReader(
            new InputStreamReader(new FileInputStream("input.csv"), "UTF-8"))) {
            
            String firstLine = br.readLine();
            
            // 最初の行の先頭にBOMがあるか確認し、あれば除去
            if (firstLine != null && firstLine.startsWith("\uFEFF")) {
                firstLine = firstLine.substring(1);
            }
            
            System.out.println(firstLine);
        }
    }
}

InputStreamReaderは指定されたエンコーディングでファイルを読み込みますが、BOMは無視しません。そのため、読み込んだ文字列からBOMを取り除く必要があります。

書き込み(output.csv)
import java.io.*;

public class WriteCSV {
    public static void main(String[] args) throws Exception {
        try (Writer writer = new OutputStreamWriter(
            new FileOutputStream("output.csv"), "UTF-8")) {
            
            // BOMを明示的に書き込む
            writer.write('\uFEFF');
            
            writer.write("名前,年齢,所属\n");
            writer.write("山田太郎,30,営業部\n");
        }
    }
}

Writerを使ってファイルに書き込む際も、BOMを表す\uFEFFを先頭に自分で書き込む必要があります。

 

C# (.NET)

C#は、Pythonほどではないにせよ、比較的簡単にBOMを扱うことができます。Encodingクラスを使うことで、BOM付きかそうでないかを意識して読み書きできます。

読み込み(input.csv)
using System;
using System.IO;
using System.Text;

class Program {
    static void Main() {
        // UTF-8を指定すると、BOMの有無を自動で判別してくれる
        string text = File.ReadAllText("input.csv", Encoding.UTF8);
        Console.WriteLine(text);
    }
}

.NET Framework.NET CoreEncoding.UTF8は、賢くBOMの有無を自動的に認識してくれます。

書き込み(output.csv)
using System;
using System.IO;
using System.Text;

class Program {
    static void Main() {
        string[] lines = {
            "名前,年齢,所属",
            "山田太郎,30,営業部",
            "鈴木花子,25,開発部"
        };
        
        // BOMを付与したUTF-8で保存
        File.WriteAllLines("output.csv", lines, new UTF8Encoding(true));
    }
}

UTF8Encoding(true)の引数をtrueにすることで、BOM付きでファイルを保存できます。falseにすればBOMなしになります。

 


 

実務での使い分け – BOM付き? BOMなし?

 

これまでBOM付きの扱いに焦点を当ててきましたが、実際の開発現場では、BOMがない純粋なUTF-8を使う場面の方が多いかもしれません。

BOM付き(UTF-8-SIG)BOMなし(UTF-8)
得意なことWindows版Excelで開いても文字化けしない多くのシステムやAPIとの互換性が高い
主な用途ExcelユーザーとCSVを共有する、手動で開くWebシステム間のデータ連携、UNIX/Linux環境での処理、コマンドラインツール

もし、「作ったCSVファイルを誰かが手作業でExcelで開く」という想定ならBOM付きがおすすめです。逆に、「別のシステムが自動で読み込む」という場合は、BOMなしの方が無用なトラブルを防げるでしょう。

 


 

まとめ

 

言語読み込み時書き込み時
Pythonutf-8-sigutf-8-sig
JavaScriptBOMを除去 (.slice(1))BOMを追加 ("\uFEFF" + ...)
JavaBOMを除去 (.substring(1))BOMを追加 (writer.write('\uFEFF'))
C#Encoding.UTF8new UTF8Encoding(true)

PythonとC#はライブラリの機能としてBOMを意識した処理が用意されており、比較的扱いやすいですね。一方、JavaScriptやJavaでは、BOMを1文字として扱うため、手動で処理を実装する必要があります。

普段使っている言語の特性を理解して、状況に合わせてBOMを使い分けられると、文字化けで困ることもグッと減るはずです。

 

本日も最後までお読みいただき、ありがとうございました。

それでは、よいCSVライフを!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール