概要
javascriptでCSVのデータを扱う必要が出てきたけど手ごろなCSVパーサーがライブラリが見つからなかったので自作してみたという内容です。
よく正規表現でパースするコードは見つかるけどダブルクオーテーションで囲われている改行とかエスケープがあるやつは対応しきれてないので、そこを解決するために自作してみました。
ツッコミどころはあるコードだとは思いますが今のところ自分の目標は達成できているので公開しておきます。
動作の保証はできませんがご自由にお使いください(いらない)
実際のコード
仕様
関数名:parseCSV
引数1:CSVテキスト(型:string)
戻り値:パースされた2次元配列
コード
csvParser.js
// csvパーサー
function parseCSV(sourceString){
// バックスラッシュエスケープ変化用関数
const escape=(escaped)=>{
// エスケープ解除された文字を格納する変数
let unescaped;
// エスケープ文字を変換
switch(escaped){
// バックスラッシュ
case "\\":{
unescaped = "\\";
}break;
// 改行
case 'n':{
unescaped = "\n";
}break;
// タブスペース
case 't':{
unescaped = "\t";
}break;
// 未対応のエスケープ
default:{
throw new Error(`unknown escape:${escaped}`);
}break;
}
return unescaped;
}
// パースした結果を配列に格納する用
const result = [];
// キャリッジリターンを消す
sourceString = sourceString.replaceAll("\r", '');
// 1文字ずつ取り出して解析
{
// 一時的な文字結合用バッファ変数
let tmpChars = '';
// 一時的な行内配列
let tmpRow=[];
// 1文字抜き出して比較
for(let idx=0; idx<sourceString.length; ++idx){
// 1文字抜き出し
const sourceChar = sourceString.charAt(idx);
switch(sourceChar){
// ダブルクオーテーションによる文字列の開始の検出
case '"':{
// ダブルクオーテーション終了に達しているかを格納する
let isEndQuote=false;
// 1文字抜き出して比較
for(let idx2=++idx; idx2<sourceString.length; ++idx2){
// 1文字抜き出し
const sourceChar2 = sourceString.charAt(idx2);
switch(sourceChar2){
// バックスラッシュエスケープの検出・変換
case "\\":{
tmpChars += escape(sourceString.charAt(++idx2));
}break;
// 次の文字がダブルクオーテーションによる文字列の終了の検出
case '"':{
// エスケープされているダブルクオーテーションの検出
if(sourceString.charAt(idx2+1)=='"'){
tmpChars += '"';
++idx2;
}
// エスケープされていないダブルクオーテーション
else{
// ダブルクオーテーション終了フラグON
isEndQuote=true;
}
}break;
// それ以外の文字
default:{
// 1文字ずつ結合していく
tmpChars += sourceChar2;
}break;
}
// 外側のループカウンタに現行の読み取り位置を記録
idx = idx2;
// ダブルクオーテーション終了に達していればループを抜ける
if(isEndQuote){
break;
}
}
}break;
// バックスラッシュエスケープの検出・変換
case "\\":{
tmpChars += escape(sourceString.charAt(++idx));
}break;
// カンマによる列の区切りを検出
case ',':{
// 行配列に結合された文字と追加・一時的な文字バッファ消去
tmpRow.push(tmpChars);
tmpChars = '';
}break;
// 改行による行の区切りを検出
case "\n":{
// 行配列に結合された文字と追加・一時的な文字バッファ消去
tmpRow.push(tmpChars);
tmpChars='';
// 現行行を配列に追加・次の行の配列を用意
result.push(tmpRow);
tmpRow=[];
}break;
// それ以外の文字
default:{
// 1文字ずつ結合していく
tmpChars += sourceChar;
}
}
}
}
return result;
}
余談
javascriptってStringBuilder的なのなかったですよね?
Javascriptの文字列結合+=は優秀と聞いた気がする(曖昧)
車輪の再発明はめんどい!(Nodeパッケージ使えよ)