#! perl # # 汎用 csv converter # by hangyo@kanazaw-bidai.ac.jp 1998-08-22 (Sat) # ver2.5 2007-10-04 (Thr) # -e オプションを付けた。複行フィールドのタブを削除しない。 # # ver2.4 2007-02-13 (Tus) # -n オプションを付けた。まったくの空データは出力しない。 # # ver2.3 2006-09-05 (Tue) # -l オプションを付けた。出力CSVファイルの1行目に各項目を表示する。 # # ver2.2 2005-02-01 (Tus) 01:12:00 # 第一フィールドが空の場合に正しくセパレータを出力しなかったバグを修正 # # ver2.1 2004-08-11 (Wed) 17:10:48 # fin 互換、行頭コマンド追加した。 # .ig 以下を無視する # .. 以下まで。 # # ver2 2003-08-13 (Wed) 01:27:15 # 複行フィールドにも対応する # (フィールドを複行に対応した。行頭のタブは無視します) # また、# 以下を削除します。 # # ver1 1998-08-22 # # 複行レコードのデータを、csvファイルに変更します。 # # 入力ファイルは、最終行にレコードセパレータがなくてはいけません。 # # アイテムセパレータは複数指定できますが、一行のなかに二つ以上ア # イテムセパレータが出てくるような行に対しては、正しい動作を保証で # きません(そういう作りは良くない)。 # セパレータを明示的に指定できます。正規表現も使える(と思う) # # -s 出力セパレータ(デフォルトは ,) # -p 入力レコードセパレータ(デフォルトは空行) # -i 入力アイテムセパレータ(デフォルトは :) # -t オプションのテストプリント # # パイプに繋げて使うときは、 - を使ってください。例えば、 # # % filtercommand infile | perl csv.pl - # #use encoding 'shift-jis'; # Usage if($#ARGV < 0){ print STDERR << "END"; usage: perl csv.pl -[s|p|i|t] infile -s 出力ファイルのフィールド・セパレータ(, カンマ) -p 入力ファイルのレコード・セパレータ(^\$ 空行) -i 入力ファイルのアイテム・セパレータ(: コロン) -e タブの保持 -t テストモード。各セパレータを示す -l 出力ファイルの1行目に項目名を出力する -n 空のレコードや空行は出力しない s p i オプション末の( ) 内は初期値 END exit; # -s output field separater (,)\ # -p input recode separater (^\$)\ # -i input item separater (:)\ # -t separater test print\ # -l print item-list on 1st Line\ # -n not print NULL record \ # (...) is default value. } # 引数の解析 while($_ = $ARGV[0], s/^-//){ shift; while(! /^$/){ # '-' の後ろの文字の解析 if (/^s(.*)$/){ $separator = $1 ||shift; last;} elsif(/^p(.*)$/){ $record_separator = $1 ||shift; last;} elsif(/^i(.*)$/){ $item = $1 ||shift; last;} elsif(/^e$/){ $tab = 1 ||shift; last;} elsif(/^t$/){ $test = 1 ||shift; last;} elsif(/^l$/){ $itset = 1 ||shift; last;} elsif(/^n$/){ $null = 1 ||shift; last;} else{print STDERR "$0 :ILLegal option(s).\n";exit;} } } # set separater if ($separator eq ""){$separator = ",";} if ($record_separator eq ""){ $record_separator = '^$';} if ($item eq ""){ $item = ":";} if ($itset){ $lflag = "ON";}else{ $lflag = "OFF";} if ($null) { $nflag = "ON";}else{ $nflag = "OFF";} # test print if($test){ print STDERR "$separator\t<- (-s) outpout fields separator\n"; print STDERR "$record_separator\t<- (-p) input record_separator\n"; print STDERR "$item\t<- (-i) intput item separator\n"; print STDERR "[$lflag]\t"."print ItemLabel in 1st line\n"; print STDERR "[$nflag]\t"."ignore null lines or recode\n"; exit; } while(<>){ chop; $rec = $_; $rec =~ s/#.*$//; # 文中の#も、以下を削除 if($rec =~ /^[#-]/){next;} # コメントアウト行は削除 next if($rec =~ /^\.ig/ .. $rec =~ /^\.\./); # fin 互換 .ig(ignore) if($rec =~ /$separator/){$sep_error++;} # セパレータを使ってたら # CSV出力 if(/$record_separator/o){ # レコード区切りが来たら、 if($itset){ # まず、項目ラベルを出力 $itall =~ s/^,//g; print "# " . "$itall" . "\n"; $itset = 0; } # print "[$itset]"; if($null < 1 || $nulf) { # NULLオプションでない時、出力 print $csv ; # データ出力 print "\n"; # 改行 } $nulf = 0; $csv = ""; # レコード区切りのフラグ } # 各項目のCSV化 if($rec =~ s/^([^$item]+)$item[\t]*//e){;# 項目ラベルと冒頭タブ削除 $nulf++ if($rec) ; # データがあれば、nullフラグ++。 if($itset){ # 第1項なら、項目ラベルをセットする。 $it = $1; $itall .= $separator . $it ; } unless($csv){ # 最初の項目なら $csv .= $separator .$rec unless($rec); # 最初フィールドがカラ $csv .= $rec; # そのまま結合 }else{ # それ以外はsep附き $csv .= $separator .$rec ; } }else{ # 項目名がない場合 $rec =~ s/^[\t ]+//g unless($tab) ; # 行頭のタブは削除(非タブモードで) $nulf++ if($rec) ; # データがあれば、nullフラグ++。 $csv .= $rec ; # フィールドはそのまま追加 } } # 最終行にレコードセパレータが無いと、警告。 if(eof==1 && $csv ne "") { print STDERR "[Worning] Inputfile needs record separator on EOF.)\n"; } # 本文中に出力用のセパレータ文字があったら、警告。 if($sep_error) { print STDERR "[Worning] Used output separator, \"$sep_error\" times.)\n"; }