WEBサイトの製作、管理、とかの日記ブログです。

<<   2023年05月   >>
SunMonTueWedThuFriSat
 123456
78910111213
14151617181920
21222324252627
28293031   
新着記事
カテゴリ
過去ログ
コメント
検索
CSVのパース
PerlでCSVをパースする時のメモ。

まず、CSVってのは、
名前のとおり「,」でデータを区切ったデータファイルだから、「,」を区切り文字にsplitすれば簡単にパースできるはずなんだが、
本来データ内に「,」を含む場合「,」はエスケープするのが筋と思うんだが、
エスケープせずに最初から文字列データをすべて「"」で囲んでるCSVが多いんですよね。
そんな糞仕様のCSVだと、区切り文字以外に「,」が含まれる可能性があるから単純に「,」でsplitはできない・・・

さらに!
"文字列1","文字列2"
な感じのCSVで、文字列の中にさらにエスケープせずに「"」が含まれる糞仕様のCSVまで・・・
こんな糞仕様だと、正規表現で抜き取ることもできませんね。

最初からダブルクォートで囲まずに、「,」をエスケープすればいいだけなのにこんなクソ仕様のCSV作るのは勘弁してほしい・・・
って感じだが、そんなCSV使いたいならどうにかしないと・・・


my(@list,$line,@line,$c,$s);
foreach $line(@file){
  $line=~s/(?<=[^,])"(?=[^,\r\n])/&#quot;/g;
  undef(@line);
  $s='';
  while(($c=substr($line,0,1,'')) ne ''){
    if($s eq '' && $c eq '"'){
      $line=~s/^([^"]*?)"[,\r\n]//;
      push(@line,$1);
      $s='';
      next;
    }
    if($c eq ','){
      push(@line,$s);
      $s='';
      next;
    }
    if($c eq "\r" || $c eq "\n"){
      push(@line,$s) if($s);
      last;
    }
    $s.=$c;
  }
  @{$list[$#list+1]}=@line;
}
@fileにはCSV全体の改行区切りの配列を入れる。(普通にCSVをopenして、@file=<FILE>のように)
$lineに1行ずつ入れてループ。
「先頭」又は「末尾」以外の「"」は&#quot;に変換。(HTML以外で使うんなら、後で戻す必要がありますね)
@lineが1行分のデータの配列とします。
1文字切り取って、$cに入れる。
先頭が「"」なら、次の「"」までをデータとして取り出して、次のデータへ。
「"」「,」以外なら、$sに文字を追加。
「,」又は改行が現れたら、$sをデータとして追加。

@listがCSV全体の2重配列としてパースが完了します。
$list[行番号][列番号]
って感じに。

ダブルクォートで囲まない、まともなCSVの中に先頭の「"」が出現するってことも考えられますから、これで無理な場合も考えられますけど、
だいたいこれでいけるんじゃないかと。

この記事へのコメント
名前:
URL
コメント:
この記事へのトラックバック :
whblog 1.7