LoginSignup
3
0

More than 3 years have passed since last update.

【javascript】「,」でsplitする際、「"~,~"」の「,」での分割を回避(追記コード2個)(修正+改行用追記)

Last updated at Posted at 2021-04-17

2021.4.22
- 追記コードの不備を修正(case 3:の処理、要素が消える)
- 改行処理用の関数を追記
- 元の関数名をcsvSplitからlienSplitに変更

前置き

javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件 - Qiita
https://qiita.com/hatorijobs/items/dd0c730e6faba0c84203

こちらの記事きっかけで書いてます。
はい、また他人の記事に乗っかってます。ごめんなさい。

CSVで読み込んだ行を「,」で分割する際の問題のようです。

このような行を
block1,block2,"¥12,345",block4,block5

こうしたいのに
=> ["block1", "block2", ""¥12,345"", "block4", "block5"]

こうなってしまう
=> ["block1", "block2", ""¥12", "345"", "block4", "block5"]

コード

最近ハマってるreduceを使って書きました。

const csvSplit = (str, s=',') => {
    str = str.replace(/"+/g,'"').split(s);      //replaceは不要なら外してもOK
    let flag = str[0].startsWith('"') - str[0].endsWith('"');       //"~"内の時「1」
    str = str.slice(1).reduce((p, x) => {
        //「"」が[1]先頭・[2]末尾または[3]両方に存在するか、[0]無いか分類し、処理
        switch (x.startsWith('"') + x.endsWith('"')*2) {
            case 0: return p.concat( flag===1 ? p.pop()+s+x : x );
            case 2: return p.concat( flag--===1 ? p.pop()+s+x : x );
            case 1: flag===1 ? p.push( ...p.pop().split(s) ) : flag = 1;
                    return p.concat(x);     //修正
            case 3: flag--;return p.concat(x);      //修正
        }
    }, [str[0]]);
    //最後の要素が閉じられてない場合は分割
    return flag===1 ? str.concat( ...str.pop().split(s) ) : str;
};

console.log( csvSplit('block1",block2",block3","¥12,345,678,900",block4,block5"') );
// ["block1"", "block2"", "block3"", ""¥12,345,678,900"", "block4", "block5""]

console.log( csvSplit(
    '"block1,block2","block3,block4,"¥12,345,678,900",block4,"block5,block6'
    ) );
// [""block1,block2"", "block3", ""¥12,345,678,900"", "block4", ""block5", "block6"]

感想

なんかごちゃごちゃしてて微妙。
気を遣う所が増えてしまったので、別のアプローチで考えたほうが良かったかもしれない。

コード(追記)

要素一つ一つを追加処理していく先のコードと違い、今回は範囲を切り出して追加していく。
切り出しの開始位置を記憶する変数startを追加。

const lienSplit = (str, s=',') => {
    let [flag, start] = [0, 0];
    str = str.replace(/"+/g,'"').split(s);
    return  str.reduce((p, x, i, a) => {
        switch (x.startsWith('"') + x.endsWith('"')*2) {
            case 1: 
                p.push( ...a.slice(start, i) );
                [flag, start] = [1, i];
                break;
            case 2: 
                if (!flag) break;
                p.push( a.slice(start, i+1).join(s) );
                [flag, start] = [0, i+1];
                break;
            case 3: 
                //修正
                p.push(i===start ? x : a.slice(start,i).concat(x));
                [flag, start] = [0,i+1];
                break;
        }
        return p;
    }, []).concat(str.slice(start));
};

↓更に追加。
switch内の類似処理が気になったので関数化してまとめました。
処理自体は上記と同じ。人によっては少し見難いかもしれない。

const lienSplit = (str, s=',') => {
    let [flag, start] = [0, 0];
    const proc = (p, e, f, t) => ([flag, start] = [f, t], p.concat(e));
    const reducer = (p, x, i, a) => {
        switch (x.startsWith('"') + x.endsWith('"')*2) {
            case 1: return proc(p, a.slice(start,i), 1, i);
            case 2: return flag ? proc(p, a.slice(start,i+1).join(s), 0, i+1) : p;
            //修正
            case 3: return proc(p, i===start ? x : a.slice(start,i).concat(x), 0, i+1);
            default: return p;
        }
    };
    str = str.replace(/"+/g,'"').split(s);
    return  str.reduce(reducer, []).concat(str.slice(start));
};

個人的に気になっているところは潰せたと思う。

改行用コード(追記)

先述の関数lineSplitの結果を元に再構成するので、改行だけしたい場合には使えない。

const csvSplit = text => {
    let [start, rest] = [0, []];
    const reducer = (p, x, i, a) => {
        if (!(x.includes('\n') && !x.startsWith('"') && !x.endsWith('"'))) return p;
        const br = x.split('\n').map(y => [y]);
        p.push( rest.concat(a.slice(start, i), br.shift()) );
        [start, rest] = [i+1, br.pop()];
        return br.length ? p.concat(br) : p;
    };
    const arr = lineSplit(text);
    return arr.reduce(reducer, []).concat([rest.concat(arr.slice(start))]);
};

やっつけで作ったので微妙。

3
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0