[JavaScript][Perl] 続・正規表現を使ったCSVパーサ
2008-01-29
正規表現を使ったCSVパーサの続き。
トークン化したCSVを解釈するコードをJavaScriptとPerlで書いた。二つのロジックはほぼ同じ。
それぞれ、第一引数には解釈するCSVテキストを渡し、第二引数ではfield間の区切り文字を指定する。デフォルトはコンマ(,)。戻り値は、CSVを解釈した2次元配列(Perlの場合はリファレンス)。
JavaScript
function parseCSV(text, delim) {
if (!delim) delim = ',';
var tokenizer = new RegExp(delim + '|\r?\n|[^' + delim + '"\r\n][^' + delim + '\r\n]*|"(?:[^"]|"")*"', 'g');
var record = 0, field = 0, data = [['']], qq = /""/g;
text.replace(/\r?\n$/, '').replace(tokenizer, function(token) {
switch (token) {
case delim:
data[record][++field] = '';
break;
case '\n': case '\r\n':
data[++record] = [''];
field = 0;
break;
default:
data[record][field] = (token.charAt(0) != '"') ? token : token.slice(1, -1).replace(qq, '"');
}
});
return data;
}
Firebugで実行した結果が↓
>>> parseCSV('aaa,"bbb"\n"c""c","d,d"\n"e\ne",,""')
[["aaa", "bbb"], ["c"c", "d,d"], ["e\ne", "", ""]]
Perl
/** parseCSV.pl */
sub parseCSV($;$) {
my($text, $delim) = (shift, shift || ',');
$text =~ s/\n$//;
my($record, $field, $data) = (0, 0, [['']]);
foreach ($text =~ /$delim|\n|[^$delim"\n][^$delim\n]*|"(?:[^"]|"")*"/g) {
$data->[$record][++$field] = '', next if ($_ eq $delim);
$data->[++$record] = [''], $field = 0, next if ($_ eq "\n");
s/^"|"$//g, s/""/"/g if (/^"/);
$data->[$record][$field] = $_;
}
return $data;
}
実行結果↓
>>> parseCSV qq/aaa,"bbb"\n"c""c","d,d"\n"e\ne",,""/ [["aaa", "bbb"], ["c\"c", "d,d"], ["e\ne", "", ""]]
一応は、
望み通りの動きをしてくれている様子。Excelで出力した素朴なCSVファイルを処理する分にはこの程度のスクリプトで充分そうだ。