JavaScript ECブログリレー

CSVファイルを読み込むときはBOM付きUTF-8に気をつける

JavaScript ECブログリレー

EC事業部エンジニアのharashoです。この記事はEC事業部ブログリレーの13日目の記事で、12日目は@ku00さんによるカラーミーショップの一機能をAngular Elementsで実装しましたでした。

タイトルがこの記事の結論になりますが、私が遭遇したCSVファイルの読み込み処理で起きた不具合と原因、対応方法について書きます。

  1. CSVファイルの読み込み処理で起きた不具合
  2. 不具合の原因
    1. BOMについて
  3. 対応方法: BOMを取り除く
  4. おわりに

CSVファイルの読み込み処理で起きた不具合

私が開発に携わったカラーミーリピートの一括発送アプリには、注文に対する発送リストをCSVファイルから一括登録して、発送処理を行う機能があります。以下はその機能の簡単な流れと、CSVファイルのイメージです。

1.CSVテンプレートをダウンロード
2.テンプレートを元に編集
3.編集したCSVファイルをアップロード
4.発送処理を実行

CSVテンプレート(サンプル)

"名前","注文ID","配送会社","伝票番号"
"ペパボ 太郎","Pi0gO9M","",""
"ペパボ 一郎","12DieQ1","",""
"ペパボ 花子","jN3rE8R","",""

編集したCSVファイル(サンプル)

"名前","注文ID","配送会社","伝票番号"
"ペパボ 太郎","Pi0gO9M","1","123456777"
"ペパボ 一郎","12DieQ1","2","123456888"
"ペパボ 花子","jN3rE8R","3","123456999"

この機能を使う際に、ダウンロードしたCSVテンプレートをExcelで編集し、UTF-8(コンマ区切り)形式で保存したCSVファイルをアップロードするとエラーが起きることが分かりました。

不具合の原因

調査した結果、CSVファイルのカラム名を変換する処理で、パースしたヘッダーのカラム名がテンプレートのヘッダーのカラム名と一致せず、エラーが起きていることが分かりました。

CSVのカラム名を変換する処理(サンプル)

switch (header) {
  case '名前': // カラム名はテンプレートと同じカラム名を期待している
    return 'name'
  // 省略
  default:
    throw new Error('ヘッダーが不正です。')
}

headerに入っている値を確認したところ、ヘッダーの最初のカラム名が、一見同じ文字列だが同じではない不思議な状態でした。

> header
"名前"

> header === "名前"
false

文字列を詳しく見るためにこの値をURIエンコードしてみると、先頭に謎の文字列 %EF%BB%BF が入っていました。これは、BOM(Byte Order Mark)というものでした。この文字列が原因でパースしたヘッダーの最初のカラム名とテンプレートのヘッダーのカラム名が一致していなかったのです。

> encodeURI(header)
'%EF%BB%BF%E5%90%8D%E5%89%8D'

> encodeURI("名前")
'%E5%90%8D%E5%89%8D'

BOMについて

バイト順マーク (バイトじゅんマーク、英: byte order mark) あるいはバイトオーダーマークとは、通称BOM(ボム)といわれるUnicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。
出典:バイト順マーク - Wikipedia

今回のケースではExcelのCSVファイルの保存形式によってBOMが付いていたようです。

対応方法: BOMを取り除く

CSVファイルにBOMが付いていても問題なく処理を行えるほうが、手間が掛からず便利です。ですので、先頭の文字列にBOMが存在する場合は取り除く処理を追加しました。sindresorhus/strip-bomというライブラリを参考に、以下のようなコードをJavaScriptで書きました。

const stringWithoutBom = csvString.charCodeAt(0) === 0xFEFF ? csvString.slice(1) : csvString

おわりに

この記事ではCSVファイルの読み込み処理で、BOMにより起きた不具合と原因、その対応方法について説明しました。私自身、今回の対応からCSVファイルのようなテキストファイルを取り扱うときはBOMの存在に注意したほうが良いことを学びました。同じ不具合に遭遇している方やCSVの読み込み処理を実装する方の参考になれば嬉しいです。