#========================================================
# 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;