#======================================================== # CSV #======================================================== package CSV; use MultiColumnData; @ISA = qw(MultiColumnData); use strict; #============================================================ # 変数等取得関数 #============================================================ #============================================================ # 静的関数 #============================================================ sub GetArraysFromFileSimple { my ($path, $IsPrint, $SkipBlankData) = @_; $IsPrint = 1 if(!defined $IsPrint); $SkipBlankData = 0 if(!defined $SkipBlankData); my $CSV = new CSV(); $CSV->SetSkipBlankData($SkipBlankData); return (undef) if(!$CSV->ReadSimple($path, undef, $IsPrint)); my $pLabelArray = $CSV->LabelArray(); my $pDataArray = $CSV->DataArray(); return (0, $pLabelArray, @$pDataArray) if(!$pDataArray); my $pdata = $pDataArray->[0]; my $nData = 0; $nData = @$pdata if($pdata); return ($nData, $pLabelArray, @$pDataArray); } sub GetArraysFromFile { my ($path, $IsNumerical, $IsPrint) = @_; $IsPrint = 1 if(!defined $IsPrint); my $CSV = new CSV(); return (undef) if(!$CSV->Read($path, $IsNumerical, $IsPrint)); my $pLabelArray = $CSV->LabelArray(); my $pDataArray = $CSV->DataArray(); return (0, $pLabelArray, @$pDataArray) if(!$pDataArray); my $pdata = $pDataArray->[0]; my $nData = 0; $nData = @$pdata if($pdata); return ($nData, $pLabelArray, @$pDataArray); } sub RemakeDataArrayFromFile { my ($path, $pX, $IsXStepConstant) = @_; my ($nData, $pLabelArray, @DataArray) = GetArraysFromFile($path); return undef if(!defined $nData); my @ConvDataArray; my $data = new GraphData; for(my $i = 1 ; $i < @DataArray ; $i++) { $data->SetXDataArray($i-1, $DataArray[0]); $data->SetYDataArray($i-1, $DataArray[$i]); $ConvDataArray[$i-1] = (); } $data->CalMinMax(); my (@e1, @e2); for(my $i = 0 ; $i < @$pX ; $i++) { my $x = $pX->[$i]; #print "$i: $x: "; for(my $id = 0 ; $id < @DataArray-1 ; $id++) { if($IsXStepConstant) { $ConvDataArray[$id]->[$i] = $data->YValForConstantXStep($id, $x); } else { $ConvDataArray[$id]->[$i] = $data->YVal($id, $x); } #print "$ConvDataArray[$id]->[$i] "; } #print "\n"; } return @ConvDataArray; } #============================================================ # コンストラクタ、デストラクタ #============================================================ BEGIN { } sub new { my ($module) = @_; my $this = {}; bless $this; $this->{SkipBlankData} = 0; return $this; } sub DESTROY { my $this = shift; } #============================================================ # 一般関数 #============================================================ sub SkipBlankData { return shift->{SkipBlankData}; } sub SetSkipBlankData { my ($this, $f) = @_; return $this->{SkipBlankData} = $f; } sub WriteLabelLine { my ($this, @LabelArray) = @_; my $line = ''; for(my $il = 0 ; $il < @LabelArray ; $il++) { my $v = $LabelArray[$il]; $v = "\"$v\"" if($v =~ /,/); $v = ",$v" if($il > 0); $line = "$line$v"; } return $this->{'JFile'}->Write("$line\n"); } sub Quote { my ($this, $str) = @_; $str = 0 unless(defined $str); $str = $str + 0.0 if($str ne '' and $str =~ /^[+-]?\d*\.?\d*[eEdD]?\d*$/); if($this->FileType() eq '' or $this->FileType() =~ /Excel/i) { $str =~ s/"/""/g; } else { $str =~ s/"/\\"/g; } return "\"$str\"" if($str =~ /[,\r\n]/); return $str; } sub WriteDataLine { my ($this, $pArray) = @_; return undef unless($this->{'JFile'}); if(!$pArray) { $pArray = $this->DataArray(); } my $nData = @$pArray; my $s = join('', @$pArray); my $code = Jcode::getcode($s); my $targetcode = 'euc-jp'; #print("code: $code
\n"); my $line = ''; for(my $i = 0 ; $i < $nData ; $i++) { my $s = $pArray->[$i]; Jcode::convert(\$s, $targetcode, $code) if($code ne '' and $code ne $targetcode and $code ne 'binary' and $code ne 'ascii'); # my $v = $s; my $v = $this->Quote($s); # my $v = ConvertToCSVFormat($pArray->[$i]); $v = ",$v" if($i > 0); $line = "$line$v"; } Jcode::convert(\$line, $code, $targetcode) if($code ne '' and $code ne $targetcode and $code ne 'binary' and $code ne 'ascii'); return $this->{'JFile'}->Write("$line\n"); return 1; } sub ConvertToCSVFormat { my @values = @_; #print "v=@values\n"; my $line = join ',', map {(s/"/""/g or /[\r\n,]/) ? qq("$_") : $_} @values; #print "l=$line\n"; return $line; } sub Open { my ($this, $filename, $mode, $IsNumerical) = @_; if(defined $IsNumerical) { $this->{IsNumerical} = $IsNumerical; } else { $this->{IsNumerical} = 0; } return $this->{JFile} = new JFile($filename, $mode); } sub Close { my ($this) = @_; return if(!$this->{JFile}); $this->{JFile}->Close(); delete $this->{JFile}; } sub eof { my ($this) = @_; return if(!$this->{JFile}); $this->{JFile}->eof(); } sub rewind { my ($this) = @_; return if(!$this->{JFile}); $this->{JFile}->rewind(); } sub ReadNextLine { my ($this) = @_; return if(!$this->{JFile}); my $in = $this->{JFile}; my $IsNumerical = $this->{IsNumerical}; my $line = $in->ReadLine(); $line .= $in->ReadLine() while ($line =~ tr/"// % 2 and !$in->eof()); my $code = Jcode::getcode($line); Jcode::convert(\$line, "utf8", $code); $line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; my @a = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); for(my $i = 0 ; $i < @a ; $i++) { my $pArray = $a[$i]; if(defined $a[$i]) { $a[$i] += 0.0 if($IsNumerical); } else { $a[$i] = ''; } Jcode::convert(\$a[$i], $code, "utf8"); } return @a; } sub GetArrayData { my ($this, $idx) = @_; my $pLabelList = $this->LabelArray(); my $pDataArray = $this->DataArray(); my $nArray = $this->nDataArray(); # my $nData = $this->nData(); #print "p=$pLabelList\n"; #print "p=$pDataArray\n"; return undef if(!defined $pDataArray->[0]->[$idx]); my @array; for(my $i = 0 ; $i < $nArray ; $i++) { $array[$i] = $pDataArray->[$i]->[$idx]; } return \@array; } sub GetHashData { my ($this, $idx) = @_; my $pLabelList = $this->LabelArray(); my $pDataArray = $this->DataArray(); my $nArray = $this->nDataArray(); # my $nData = $this->nData(); #print "p=$pLabelList\n"; #print "p=$pDataArray\n"; return undef if(!defined $pDataArray->[0]->[$idx]); my %hash; for(my $i = 0 ; $i < $nArray ; $i++) { $hash{$pLabelList->[$i]} = $pDataArray->[$i]->[$idx]; } return \%hash; } sub FindLabelIndex { my ($this, $pLabels, $pLabelArray) = @_; $pLabelArray = $this->LabelArray() if(!defined $pLabelArray); my $nLabelArray = @$pLabelArray; for(my $l = 0 ; $l < @$pLabels ; $l++) { my $label = $pLabels->[$l]; for(my $i = 0 ; $i < $nLabelArray ; $i++) { if($pLabelArray->[$i] eq $label or $pLabelArray->[$i] =~ /^$label$/i) { return $i; } } } return undef; } sub nValidItems { my $n = 0; for(my $i = 0 ; $i < @_ ; $i++) { $n++ if($_[$i] ne ''); } return $n; } sub ReadFreeFormat { my ($this, $filename, $separator, $pLabelArray, $IsNumerical, $IsPrint, $nMinimumLabels) = @_; $separator = "\\s+" if(!defined $separator); $IsNumerical = 1 if(!defined $IsNumerical); $IsPrint = 1 if(!defined $IsPrint); $filename = $this->FilePath() unless($filename); $nMinimumLabels = 1 if(!defined $nMinimumLabels); my $in = $this->Open($filename, "r", undef, $IsPrint); return undef unless($in); my $SkipBlankData = $this->SkipBlankData(); my @LabelList; if($pLabelArray) { @LabelList = @$pLabelArray; } else { my $line = $in->ReadLine(); return undef unless($line); Utils::DelSpace($line); @LabelList = Utils::Split($separator, $line); my $nc = &nValidItems(@LabelList); last if($nc >= $nMinimumLabels); } #print "line: $line\n"; #print "Ll: ", join(':', @LabelList), "\n"; my @DataArray; for(my $i = 0 ; $i < @LabelList ; $i++) { $DataArray[$i] = []; #\@array; } my $c = 0; while(!$in->eof()) { my $line = $in->ReadLine(); Utils::DelSpace($line); my @a = Utils::Split($separator, $line); # next if(@a <= $nMinimumLabels); my $ValidDataExist = 0; for(my $i = 0 ; $i < @LabelList ; $i++) { next if($IsNumerical and $a[$i] !~ /^[\d\+\-\.eEdD]+$/); if(defined $a[$i] and $a[$i] ne '') { $a[$i] += 0.0 if($IsNumerical); $ValidDataExist = 1; } elsif(!$SkipBlankData) { $a[$i] = ''; $ValidDataExist = 1; } my $pArray = $DataArray[$i]; $pArray->[$c] = $a[$i]; } $c++ if(!$SkipBlankData or $ValidDataExist); } $this->Close(); $this->SetFileType("FreeFormat"); $this->SetLabelArray(\@LabelList); $this->SetDataArray(\@DataArray); $this->BuildLabelIndexHash(); return 1; } sub Read { my ($this, $filename, $IsNumerical, $IsPrint, $nMinimumLabels, $ReadCheckFunc, %Args) = @_; $IsNumerical = 1 if(!defined $IsNumerical); $IsPrint = 1 if(!defined $IsPrint); $filename = $this->FilePath() unless($filename); $nMinimumLabels = 1 if(!defined $nMinimumLabels); my $in = $this->Open($filename, "r", undef, $IsPrint); return undef unless($in); my $SkipBlankData = (defined $Args{SkipBlankData})? $Args{SkipBlankData} : $this->SkipBlankData(); my @LabelList = (); if($Args{nSkipLinesBeforeHeader} > 0) { for(my $i = 0 ; $i < $Args{nSkipLinesBeforeHeader} ; $i++) { $in->ReadLine(); } } #print "[$IsNumerical][$nMinimumLabels]\n"; while(1) { my $line = $in->ReadLine(); return undef unless($line); Utils::DelSpace($line); @LabelList = split(/,/, $line); my $nc = &nValidItems(@LabelList); last if($nc >= $nMinimumLabels); } if($Args{nSkipLinesAfterHeader} > 0) { for(my $i = 0 ; $i < $Args{nSkipLinesAfterHeader} ; $i++) { $in->ReadLine(); } } #print "line: $line\n"; #print "Ll: ", join(':', @LabelList), "\n"; my @DataArray; for(my $i = 0 ; $i < @LabelList ; $i++) { $DataArray[$i] = []; #\@array; } my $c = 0; while(!$in->eof()) { my $line = $in->ReadLine(); $line .= $in->ReadLine() while ($line =~ tr/"// % 2 and !$in->eof()); my $code = Jcode::getcode($line); #print "code: $code: [$line]
\n"; Jcode::convert(\$line, "utf8", $code) if($code ne '' and $code ne 'binary'); $line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; my @a = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); my $ValidDataExist = 0; if($ReadCheckFunc) { my $ret = &$ReadCheckFunc($this, \@LabelList, \@DataArray, \@a); if($ret == -1) { last; } elsif($ret == 0) { next; } } my $nc = (@a > @LabelList)? @a : @LabelList; for(my $i = @LabelList ; $i < $nc ; $i++) { $DataArray[$i] = [] if(!defined $DataArray[$i]); } for(my $i = 0 ; $i < $nc ; $i++) { # for(my $i = 0 ; $i < @LabelList ; $i++) { next if($IsNumerical and $a[$i] !~ /^[\d\+\-\.eEdD]+$/); if(defined $a[$i] and $a[$i] ne '') { $a[$i] += 0.0 if($IsNumerical); $ValidDataExist = 1; } elsif(!$SkipBlankData) { $a[$i] = ''; $ValidDataExist = 1; } Jcode::convert(\$a[$i], $code, "utf8") if($code ne '' and $code ne 'binary'); my $pArray = $DataArray[$i]; #print "i=$i
\n"; $pArray->[$c] = $a[$i]; } $c++ if(!$SkipBlankData or $ValidDataExist); } $this->Close(); #print "n = ", scalar @DataArray, "\n"; $this->SetFileType("CSV"); $this->SetLabelArray(\@LabelList); $this->SetDataArray(\@DataArray); $this->BuildLabelIndexHash(); return 1; } # 特殊文字("\"", "," ,"改行"を含まない場合) sub ReadSimple { my ($this, $filename, $IsNumerical, $IsPrint) = @_; $IsNumerical = 1 if(!defined $IsNumerical); $IsPrint = 1 if(!defined $IsPrint); $filename = $this->FilePath() unless($filename); my $SkipBlankData = $this->SkipBlankData(); my $in = $this->Open($filename, "r", undef, $IsPrint); return undef unless($in); my $line = $in->ReadLine(); return undef unless($line); Utils::DelSpace($line); my @LabelList = split(/,/, $line); my @DataArray; for(my $i = 0 ; $i < @LabelList ; $i++) { $DataArray[$i] = []; #\@array; } my $c = 0; while(!$in->eof()) { my $line = $in->ReadLine(); chomp($line); my @a = split(/,/, $line); my $ValidDataExist = 0; for(my $i = 0 ; $i < @LabelList ; $i++) { my $pArray = $DataArray[$i]; if(defined $a[$i] and $a[$i] ne '') { #print "+ a[$c][$i]=[$a[$i]] S=$SkipBlankData\n"; $a[$i] += 0.0 if($IsNumerical); $ValidDataExist = 1; } elsif(!$SkipBlankData) { #print "x a[$c][$i]=[$a[$i]]\n"; $a[$i] = ''; $ValidDataExist = 1; } else { # $ValidDataExist = 0; } $pArray->[$c] = $a[$i]; } if($SkipBlankData and !$ValidDataExist) { #print "x$c: S=$SkipBlankData, $ValidDataExist\n"; for(my $i = 0 ; $i < @LabelList ; $i++) { my $pArray = $DataArray[$i]; delete $pArray->[$c]; } } else { #print "+$c: S=$SkipBlankData, $ValidDataExist\n"; $c++; } } $this->Close(); my $p = $DataArray[0]; my $nData = @$p; #print "c=$c\n"; #print "n=$nData\n"; for(my $i = 0 ; $i < $nData ; $i++) { # print "$i: "; for(my $j = 0 ; $j < @LabelList ; $j++) { # print "$DataArray[$j]->[$i], "; if(!defined $DataArray[$j]->[$i]) { #print "i,j=$i,$j\n"; if($IsNumerical) { $DataArray[$j]->[$i] = 0.0; } else { $DataArray[$j]->[$i] = ''; } } elsif($IsNumerical and $DataArray[$j]->[$i] !~ /^[+\-\d\.eE]+$/) { #print "+i,j=$i,$j [$DataArray[$j]->[$i]]\n"; $DataArray[$j]->[$i] = 0.0; } } # print "\n"; } #print "c=$c\n"; #print "N=$IsNumerical\n"; $this->SetFileType("CSV"); $this->SetLabelArray(\@LabelList); $this->SetDataArray(\@DataArray); $this->BuildLabelIndexHash(); return 1; } # 特殊文字("\"", "," ,"改行"を含まない場合) sub ReadSimple_prev { my ($this, $filename, $IsNumerical, $IsPrint) = @_; $IsNumerical = 1 if(!defined $IsNumerical); $IsPrint = 1 if(!defined $IsPrint); $filename = $this->FilePath() unless($filename); my $in = $this->Open($filename, "r", undef, $IsPrint); return undef unless($in); my $line = $in->ReadLine(); return undef unless($line); Utils::DelSpace($line); my @LabelList = split(/,/, $line); #print "line: $line\n"; #print "Ll: ", join(':', @LabelList), "\n"; my @DataArray; for(my $i = 0 ; $i < @LabelList ; $i++) { # my @array; $DataArray[$i] = []; #\@array; } my $c = 0; while(!$in->eof()) { my $line = $in->ReadLine(); Utils::DelSpace($line); my @a = split(/,/, $line); # my $pArray = $DataArray[$c] = []; for(my $i = 0 ; $i < @LabelList ; $i++) { my $pArray = $DataArray[$i]; # next unless($pArray); if(defined $a[$i]) { $a[$i] += 0.0 if($IsNumerical); } else { $a[$i] = ''; } $pArray->[$c] = $a[$i]; # $pArray->[$i] = $a[$i]; #print " $LabelList[$i]: $a[$i]\n"; #return if($c > 2); } $c++; } $this->Close(); #print "n = ", scalar @DataArray, "\n"; $this->SetFileType("CSV"); $this->SetLabelArray(\@LabelList); $this->SetDataArray(\@DataArray); $this->BuildLabelIndexHash(); return 1; } sub Save { my ($this, $filename, $pLabelArray, $pArray, $OutputInterval) = @_; $OutputInterval = 1 if(!defined $OutputInterval or $OutputInterval <= 0); $filename = $this->FilePath() unless($filename); my $out = $this->Open($filename, "w"); return undef unless($out); if(!defined $pLabelArray) { $pLabelArray = $this->LabelArray(); } if(!defined $pArray) { $pArray = $this->DataArray(); } #my $n = $this->nData(); #print "n=$n, ", scalar @$pArray, "\n"; #exit; #一行目のラベルを作る if($pLabelArray) { $this->WriteLabelLine(@$pLabelArray); } if(!$pArray) { $pArray = $this->DataArray(); } my $pData0 = $pArray->[0]; my $nData = 0; if($pData0) { $nData = @$pData0; } my $nDataArray = 0; if($pLabelArray) { $nDataArray = @$pLabelArray; } for(my $i = 1 ; $i < $nDataArray ; $i++) { my $pData = $pArray->[$i]; last if(!defined $pData); $nData = @$pData if($nData < @$pData); } for(my $i = 0 ; $i < $nData ; $i++) { next if($i % $OutputInterval != 0); my $line = ''; for(my $il = 0 ; $il < @$pArray ; $il++) { my $pData = $pArray->[$il]; my $v = $pData->[$i]; $v = '' if(!defined $v); $v = $this->Quote($v); $v = ",$v" if($il > 0); $line = "$line$v"; } $out->Write("$line\n"); } $this->Close(); return 1; } 1; __END__ package CSV; use strict; use Carp; sub TIEARRAY($) { my ($class, $csvfile) = @_; my $this = { 'DATA' => [], 'FILE' => $csvfile }; if( open(FI, "< $csvfile") ) { my ($fld, @fld); while( ) { s/[\r\n]$//; @fld = (); for(;;) { if( s/^"((?:[^"]|"")*)"// ) { ($fld, $_) = ($1, $'); $fld =~ s/""/"/g; } else { /^([^,]*)/; ($fld, $_) = ($1, $'); } push @fld, $fld; last if length($_) <= 0; carp("CSV format error") unless s/^,//; } push @{$this->{DATA}}, [ @fld ]; } } return bless $this, $class; } sub FETCH($) { my ($this, $idx) = @_; return undef if $idx < 0 || $#{$this->{DATA}} < $idx; return $this->{DATA}[$idx]; } sub STORE($$) { my ($this, $idx, $val) = @_; return if $val eq undef || $idx < 0; carp("val error"), return if 'ARRAY' ne ref($val); $this->{DATA}[$idx] = $val; } sub DESTROY() { my ($this) = @_; $this->sync(); } sub sync() { my ($this) = @_; unless( open(FO, ">$this->{FILE}") ) { carp("open(>$this->{FILE}) failure"); } else { my ($i, $j, $rec, $fld, $buf); for($i = 0 ; $i <= $#{$this->{DATA}} ; $i++) { $rec = ''; if($this->{DATA}[$i]) { foreach $fld (@{$this->{DATA}[$i]} ) { $buf = $fld; if($buf =~ /[,"]/) { $buf =~ s/"/""/g; $buf = '"' . $buf . '"'; } $rec .= ",$buf"; } $rec =~ s/^,//; } print FO "$rec\n"; } close(FO); } } sub clear() { my ($this) = @_; $this->{DATA} = []; } sub rec() { my ($this) = @_; return scalar @{$this->{DATA}}; } 1;