Ribbit.work

ダブルクォートの有無や文字コードを問わず使用できるCSV取込

javascript

last modified date2021-7-20

publish date2021-7-19

こんにちはリビットです。

いきなりですが、CSV取込って難しくありませんか?

取り込む際はFileオブジェクトを取り扱わないといけませんし、stringとして取得できた後も、データの中にカンマが含まれていたり、ダブルクォートで囲まれてたり囲まれていなかったり…

考慮することが多いです。

JavaScriptを使ったCSV取込について検索して調べてみたんですが、古い情報が載っていたり、場合によっては正常に取り込めなかったり。

ということで今回は各文字コードに対応し、かつダブルクォートの有無にかかわらずCSVを取り込むことのできるコードを紹介します。 ちなみにですが取り込むCSVデータは、

<input type="file" />

上記のような入力から取得できるFileオブジェクトを使うことを前提としています。

CSVファイルからテキストデータを取得する

CSVファイルからString型のデータを取得する部分です。

まずコード全文をご覧ください。

/**
 @param {File} file
 @param {string} encoding
 @return {Promise<string | undefined>}
*/
const getTextFromCSV = (file, encoding = "Shift_JIS") => {
  if (encoding === "UTF-8") {
    return file.text();
  }

  return new Promise((resolve, reject) => {
    try {
      const reader = new FileReader();

      reader.readAsText(file, encoding);

      reader.onload = (event) => resolve(event.target?.result);
      reader.onerror = (event) => reject(event);
    } catch (error) {
      reject(error);
    }
  });
};

CSVファイルの文字コードがUTF-8であれば、Blob.text()を使用できます。 FileはBlobのラッパークラスなので、そのままFile.text()として使用できます。

文字コードがUTF-8以外の場合は、イベントベースのFileReaderを使うしかありません。

FileReaderであればUTF-8も処理できてしまうのでBlob.text()を使う必要はなさそうですが、UTF-8エンコードのファイルからデータを取得する際はこちらが推奨されているので、このようなコードになっています。

扱い方を統一させるため、戻り値はいずれのエンコードの場合もPromise<string | undefined>です。

データを配列に変換する

続いて取得したstring型の変数を二次元配列に変換します。

/**
 @param {File} file
 @param {string} encoding
 @return {string[][]}
*/
const getArrayFromCSV = async (file, encoding = "Shift_JIS") => {
  const text = await getTextFromCSV(file, encoding);

  if (!text || typeof text !== "string") {
    throw new Error("ファイルを配列に変換できませんでした");
  }

  const arr = text.split(/\r?\n/).map((row) => {
    const wrapsQuat =
      row.indexOf('"') === 0 && row.lastIndexOf('"') === row.length - 1;

    if (wrapsQuat) {
      return row.substring(1, row.length - 1).split('","');
    }
    return row.split(",");
  });

  return arr;
};

CSVファイルの改行コードもCRLFとLFのパターンがあるので、正規表現を使って

str.split(/\r?\n/)

のように設定しています。

時々、特定の行はダブルクォーテーションで覆い、そのほかの行はカンマのみ。といった曲者もいるので、ダブルクォーテーションの判定は行ごとにおこなっています。

row.indexOf('"') === 0 && row.lastIndexOf('"') === row.length - 1;

ダブルクォーテーションで覆われている場合は、まず先頭と末尾を削除し, データ間のダブルクォーテーションはカンマと合わせて削除するようにしています。

この記事をシェア

この記事を読んだ方におすすめの記事

スプレッド構文とconcat、どちらを使うべきか【javascript】
2021-8-11

スプレッド構文とconcat、どちらを使うべきか【javascript】

javascript
Kintoneのフィールドコードの命名規則と、JavaScript上で使用する際に気を付けること
2021-7-9

Kintoneのフィールドコードの命名規則と、JavaScript上で使用する際に気を付けること

Kintonejavascript
1つのJavaScriptファイルに複数のkintone.events.onを登録する
2021-7-5

1つのJavaScriptファイルに複数のkintone.events.onを登録する

Kintonejavascript
kintone.events.onを書かないkintoneカスタマイズ
2021-6-27

kintone.events.onを書かないkintoneカスタマイズ

Kintonejavascript

最新の記事

kuromoji.jsで形態素解析
2021-12-4

kuromoji.jsで形態素解析

TypeScriptJavaScript
レコードのサブテーブルを並び替えて登録する【Kintone JavaScriptカスタマイズ】
2021-12-4

レコードのサブテーブルを並び替えて登録する【Kintone JavaScriptカスタマイズ】

JavaScriptKintone
レコード一覧表示時(app.record.index.show)のサンプルコード集【Kintone】
2021-11-29

レコード一覧表示時(app.record.index.show)のサンプルコード集【Kintone】

JavaScriptKintone
スライダーを使って数値を設定できるKintoneプラグイン【無料公開】
2021-11-26

スライダーを使って数値を設定できるKintoneプラグイン【無料公開】

KintoneJavaScriptTypeScript