[経緯] 執筆までのいきさつ
ボス:うちで開発中のシステムの対向システムがどうやらBOM付きのCSVを渡すらしいから、こっちもBOM付きのままのCSVを受け取って今まで通り処理出来るようにしてくれやー
筆者:仰せのままに
筆者(心の中):ボム(BOM)って言った?ドム(DOM)ならフロント周りとかでちょっとは知ってるけど、BOMってなんだ??
筆者:うーん。うちのシステムはJavaだし、ひとまず「Java ボム付き CSV」って検索するか
筆者:カタカタカタカタ…。一応、なんとかなったー
ボス:今年もクリスマスの時期が来るらしいから、この1年で学んだことの一つや二つをQiitaに投稿してくれやー
筆者:仰せのままに
筆者(心の中):せっかくだし、さっきやったBOMのことを備忘録としてまとめておこう!
[概要] BOMの正体を探る
①BOMとは
正式名称は、バイト・オーダー・マーク(byte order mark)
Unicode()で記載されたテキストファイルの先頭に書かれている、そのファイルの形式を識別する特別な文字列(バイト)のようなイメージ
UTF-16の場合) 上記の文字列によって、該当のテキストファイルを解釈する際の順番が異なる
UTF-8 の場合) 上記の文字列によって、該当のテキストファイルがUTF-8が使われていることがわかる
→今回はUTF-8の場合に関して取り上げます!
②BOM付き/無しによる差異 ※一部抜粋
◆BOM無し
- CSVやTSVファイルをExcelで開いた際に文字化けするため、利用者は内容を認識出来ない
- ファイル読み込み時の通常通りの処理で問題無し
◆BOM付き
- CSVやTSVファイルをExcelで開いた際に文字化けしないため、利用者は内容を認識しやすい
- システムがBOM付きの考慮をしていないと、テキストファイル読み込み時にバグが起きる?
→上記の 1 で記載したように、Excel側がBOMを読み込んでUTF-8でExcelを表示している。そのため、UTF-8のBOM付きはテキストファイル先頭に識別のための特別な文字列が付与されているイメージのため、テキストファイルを読み込む処理を実施時にBOM付きへの考慮がされていないとBOM文字列 + テキストファイル先頭の内容、と本来の読み込む内容に加えてBOMが読み取られるため、BOMを削除してから読み込む等の考慮が無いとシステムが想定外の動きを起こしてしまう。
◆テキストエディタやExcelで開いた際のイメージ
サクラエディタで開いた際にはUTF-8 BOM付と記載される。
設定 > 文字コードセット設定 > 文字コードセットからUTF-8 を選択、BOMにチェック
BOM付きのテキストファイルをExcelで開くと、文字化けせず読みやすい
[処理例] Javaでの書き方
主に2つ、①テキストファイルを読み取った際にテキストファイル先頭に記載されているBOMを削除して元々のテキストファイルの内容の読み取りにつなげる処理、②諸々の内容を記載し最後にテキストファイルの先頭にBOMを付与する際の処理を参考記事を元に紹介します。
①BOM付きテキストの読み取り
・処理概要:テキストファイルのN行目を引数で貰い、先頭文字からBOMを削除して返却
public class ReadBOM {
/**
* BOM付きのテキストをBOM無しに変換 ※参考記事から抜粋
*/
public static void read(String row) {
String bom = row.substring(0, 1);
// 先頭文字をバイトを文字に変換する(Apache Commons CodecのHexクラスを利用)
String bomByte = new String(Hex.encodeHex(bom.getBytes()));
if ("efbbbf".equals(bomByte)) {
//BOMを排除
row = row.substring(1);
}
System.out.println(row);
}
}
②テキストファイルへのBOMの書き込み
・処理概要:内容が書き込まれたテキストファイルの先頭3byteにBOM、「0xef」、「0xbb」、「0xbf」を埋め込む
public class AddBOM {
/**
* BOM付きのCSVファイルを作成(文字コードはUTF-8)※参考記事から抜粋
*
* @param
* @return
*/
public static void main(String[] args) {
try(FileOutputStream fos = new FileOutputStream("アウトプットファイルのパス");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
PrintWriter writer = new PrintWriter(osw)){
// テキストファイル先頭3byteにBOM(「0xef」、「0xbb」、「0xbf」)記載
fos.write(0xef);
fos.write(0xbb);
fos.write(0xbf);
} catch (IOException e) {
System.out.println("ファイルの生成に失敗。");
}
}
}
参考記事
・BOM (Byte Order Mark) - XML用語事典 - ITmedia
https://atmarkit.itmedia.co.jp/aig/01xml/bom.html
https://alaki.co.jp/blog/?p=1236
・BOM付きのCSVインポート
https://qiita.com/tomoya-misuda/items/49c251b717b0c697634a