CSVのパース

Ads

面倒い問題

  • CSV のパースは一見簡単だがダブルクオートで囲った形式も扱おうとすると厄介な問題である。
  • 毎回悩むのでここにパース用関数をメモっておく。

パース関数の実装

ここでは以下の条件を満たす形式の CSV を対象とします。 *1

  • 区切りはカンマである。
  • データにカンマを含む場合はダブルクオートで囲う。
  • ダブルクオートで囲ったデータ中のダブルクオートはダブルクオート2回で置き換える。

PHP版

/**
 * CSV の1行をパースします。
 * この関数が対応しているCSVの行形式は以下の通りです。
 * ・区切りはカンマである。
 * ・データにカンマを含む場合はダブルクオートで囲う。
 * ・ダブルクオートで囲ったデータ中のダブルクオートはダブルクオート2回で置き換える。
 *
 * @return CSV をパースした結果の配列
 */
function parse_csv($line) {
	preg_match_all('/("[^"]*(?:""[^"]*)*"|[^,]*),?/', $line, $a);
	foreach($a[1] as $key => $value) {
		if(preg_match('/^"(.*)"$/', $value, $value2)) {
			$a[1][$key] = preg_replace('/""/', '"', $value2[1]);
		}
	}
	return $a[1];
}

実行例

$line = 'hdjks,d"as,"a,a","b""b",d,';
print_r(parse_csv($line));

Perl版

#
# CSV の1行をパースします。
# この関数が対応しているCSVの行形式は以下の通りです。
# ・区切りはカンマである。
# ・データにカンマを含む場合はダブルクオートで囲う。
# ・ダブルクオートで囲ったデータ中のダブルクオートはダブルクオート2回で置き換える。
# 
# @return CSV をパースした結果の配列
#
sub parse_csv() {
    my $line = shift();
    my @a = ();
    while($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),?/g) {
        $_ = $1;
        s/^"(.*)"$/$1/ && s/""/"/g;
        push(@a, $_);
    }
    if($columns[-1] eq '') {
        pop(@a);
    }
    return @a;
}

実行例

$line = 'hdjks,d"as,"a,a","b""b",d,';
print join("\n", parse_csv($line));

コメント



*1 エクセルのCSV吐き出しの形式がこれのようです。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-03-08 (月) 12:12:28 (3202d)