背景
業務でCSV取込機能を作っていて、なめてかかっていたら詰まったので備忘録として残します
課題
以下のようなCSVを取り込む必要があり、
"dummy","",,dummy
結果として、
[
"dummy",
"",
null,
"dummy",
]
を得たいとのこと
え…CSVはそもそも文字列しか扱えないものだと思ってた…null
も考慮せなあかんの…
PHPで頑張る
PHPにはfgetcsv
とstr_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はパーサー自作するしかないと思ってたのですが、パースはやっぱり標準関数に任せたいと思いこの方法になりました。(知り合いが案くれました)
でもやっぱりなんちゃって感が凄い
めちゃくちゃ不安
誰か良い方法あったら教えてください!