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

こんにちはリビットです。
いきなりですが、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;
ダブルクォーテーションで覆われている場合は、まず先頭と末尾を削除し, データ間のダブルクォーテーションはカンマと合わせて削除するようにしています。