0
1

CSVのパースで空文字とnullを区別する

Posted at

背景

業務でCSV取込機能を作っていて、なめてかかっていたら詰まったので備忘録として残します

課題

以下のようなCSVを取り込む必要があり、

"dummy","",,dummy

結果として、

[
 "dummy",
 "",
 null,
 "dummy", 
]

を得たいとのこと
え…CSVはそもそも文字列しか扱えないものだと思ってた…nullも考慮せなあかんの…

PHPで頑張る

PHPにはfgetcsvstr_getcsvがあるけど、

どっちも以下のような結果になる

str_getcsv('"dummy","",,dummy');
/*
[
 "dummy",
 "",
 "",
 "dummy", 
]
*/

公式のオプション見て色々試したものの、全然上手くいかないので凄く強引だけど以下で解決しました

function parseCsvNullHandling($line): array
{
    $pattern = '/(?<=,|^)""(?=,|$)/';
    $replacement = '___BLANK_TEXT___';
    return array_map(function ($v) use($replacement) {
        return $v === '' ? null : ($v === $replacement ? '' : $v);
    }, str_getcsv(preg_replace($pattern, $replacement, $line)));
}

parseCsvNullHandling('"dummy","",,dummy');
/*
array(4) {
  [0]=>
  string(5) "dummy"
  [1]=>
  string(0) ""
  [2]=>
  NULL
  [3]=>
  string(5) "dummy"
}
*/

①正規表現で開始位置"",,"",,""終端""___BLANK_TEXT___に置換
str_getcsvでパース後、空文字null___BLANK_TEXT___空文字に変換

とりあえず出来たけど、考慮が漏れてそうでとても怖い…

TypeScriptで頑張る

TypeScriptではparse-csvを使用しました

こっちも特に何も考慮しないとPHPと同じ結果になった

parse('"dummy","",,dummy', {
    escape: "\\"
})
/*
[ [ 'dummy', '', '', 'dummy' ] ]
*/

こっちはcastオプションでクォートの有無が確認出来たのですんなり出来た

import {parse} from "csv-parse/sync";

parse('"dummy","",,dummy', {
    escape: "\\",
    cast: (value, { quoting }) => {
        return value === '' && !quoting ? null : value;
    }
})
/*
[ [ 'dummy', '', null, 'dummy' ] ]
*/

感想

PHPはパーサー自作するしかないと思ってたのですが、パースはやっぱり標準関数に任せたいと思いこの方法になりました。(知り合いが案くれました)
でもやっぱりなんちゃって感が凄い
めちゃくちゃ不安
誰か良い方法あったら教えてください!

0
1
0

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
0
1