根据列值perl text :: csv拆分文件

时间:2015-01-08 14:58:21

标签: perl csv

我之前已经问过this question如何使用AWK做这件事,但它并没有很好地处理它。 数据在引用字段中有分号,AWK没有考虑这些分号。所以我使用text :: csv模块在perl中尝试它,所以我不必考虑这个。问题是我不知道如何根据列值将其输出到文件。

上一个问题的简短例子,数据:

10002394;"""22.98""";48;New York;http://testdata.com/bla/29012827.jpg;5.95;93962094820
10025155;27.99;65;Chicago;http://testdata.com/bla/29011075.jpg;5.95;14201021349
10003062;19.99;26;San Francisco;http://testdata.com/bla/29002816.jpg;5.95;17012725049
10003122;13.0;53;"""Miami""";http://testdata.com/bla/29019899.jpg;5.95;24404000059
10029650;27.99;48;New York;http://testdata.com/bla/29003007.jpg;5.95;3692164452
10007645;20.99;65;Chicago;"""http://testdata.com/bla/28798580.jpg""";5.95;10201848233    
10025825;12.99;65;Chicago;"""http://testdata.com/bla/29017837.jpg""";5.95;93962025367

期望的结果:

File --> 26.csv
10003062;19.99;26;San Francisco;http://testdata.com/bla/29002816.jpg;5.95;17012725049

File --> 48.csv
10002394;22.98;48;New York;http://testdata.com/bla/29012827.jpg;5.95;93962094820
10029650;27.99;48;New York;http://testdata.com/bla/29003007.jpg;5.95;3692164452

File --> 53.csv
10003122;13.0;53;Miami;http://testdata.com/bla/29019899.jpg;5.95;24404000059

File --> 65.csv
10025155;27.99;65;Chicago;http://testdata.com/bla/29011075.jpg;5.95;14201021349
10007645;20.99;65;Chicago;http://testdata.com/bla/28798580.jpg;5.95;10201848233    
10025825;12.99;65;Chicago;http://testdata.com/bla/29017837.jpg;5.95;93962025367

这是我到目前为止所拥有的。 编辑:修改后的代码:

#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV_XS;
#use Data::Dumper;
use Time::Piece;

my $inputfile  = shift || die "Give input and output names!\n";

open my $infile, '<', $inputfile or die "Sourcefile in use / not found :$!\n";

#binmode($infile, ":encoding(utf8)");

my $csv = Text::CSV_XS->new({binary => 1,sep_char => ";",quote_space => 0,eol => $/});

my %fh;
my %count;
my $country;
my $date = localtime->strftime('%y%m%d');

open(my $fh_report, '>', "report$date.csv");

$csv->getline($infile);

while ( my $elements = $csv->getline($infile)){

EDITED IN:
__________ 
next unless ($elements->[29] =~ m/testdata/);

for (@$elements){
        next if ($elements =~ /apple|orange|strawberry/);
        }
__________

for (@$elements){
        s/\"+/\"/g;
        }

    my $filename = $elements->[2];
    $shop = $elements->[3] .";". $elements->[2];

    $count{$country}++;

        $fh{$filename} ||= do {
            open(my $fh, '>:encoding(UTF-8)', $filename . ".csv") or die "Could not open file '$filename'";
            $fh;
        };

    $csv->print($fh{$filename}, $elements); 
    }

    #print $fh_report Dumper(\%count);
    foreach my $name (reverse sort { $count{$a} <=> $count{$b} or $a cmp $b } keys %count) {
        print $fh_report "$name;$count{$name}\n";
    }

close $fh_report;

错误:

Can't call method "print" on an undefined value at sort_csv_delimiter.pl line 28, <$infile> line 2

我一直在搞乱这个,但我完全不知所措。有人能帮助我吗?

2 个答案:

答案 0 :(得分:1)

我的猜测是你想要缓存文件句柄的哈希值,

my %fh;
while ( my $elements = $csv->getline( $infile ) ) {

  my $filename = $elements->[2];

  $fh{$filename} ||= do {
    open my $fh, ">", "$filename.csv" or die $!;
    $fh;
  };

  # $csv->combine(@$elements);
  $csv->print($fh{$filename}, $elements);     
}

答案 1 :(得分:0)

我没有看到您声明的问题的实例 - 在引用字段中出现分号分隔符; - 但您是正确的Text::CSV将正确处理它。

这个简短的程序从DATA文件句柄中读取您的示例数据,并将结果打印到STDOUT。如果您愿意,我假设您知道如何读取或写入不同的文件。

use strict;
use warnings;

use Text::CSV;

my $csv = Text::CSV->new({ sep_char => ';', eol => $/ });

my @data;

while ( my $row = $csv->getline(\*DATA) ) {
  push @data, $row;
}

my $file;

for my $row ( sort { $a->[2] <=> $b->[2] or $a->[0] <=> $b->[0] } @data ) {
  unless (defined $file and $file == $row->[2]) {
    $file = $row->[2];
    printf "\nFile --> %d.csv\n", $file;
  }
  $csv->print(\*STDOUT, $row);
}

__DATA__
10002394;22.98;48;http://testdata.com/bla/29012827.jpg;5.95;93962094820
10025155;27.99;65;http://testdata.com/bla/29011075.jpg;5.95;14201021349
10003062;19.99;26;http://testdata.com/bla/29002816.jpg;5.95;17012725049
10003122;13.0;53;http://testdata.com/bla/29019899.jpg;5.95;24404000059
10029650;27.99;48;http://testdata.com/bla/29003007.jpg;5.95;3692164452
10007645;20.99;65;http://testdata.com/bla/28798580.jpg;5.95;10201848233    
10025825;12.99;65;http://testdata.com/bla/29017837.jpg;5.95;93962025367

<强>输出

File --> 26.csv
10003062;19.99;26;http://testdata.com/bla/29002816.jpg;5.95;17012725049

File --> 48.csv
10002394;22.98;48;http://testdata.com/bla/29012827.jpg;5.95;93962094820
10029650;27.99;48;http://testdata.com/bla/29003007.jpg;5.95;3692164452

File --> 53.csv
10003122;13.0;53;http://testdata.com/bla/29019899.jpg;5.95;24404000059

File --> 65.csv
10007645;20.99;65;http://testdata.com/bla/28798580.jpg;5.95;"10201848233    "
10025155;27.99;65;http://testdata.com/bla/29011075.jpg;5.95;14201021349
10025825;12.99;65;http://testdata.com/bla/29017837.jpg;5.95;93962025367

<强>更新

我刚刚意识到您的“期望结果”不是您期望看到的输出,而是单独记录写入不同文件的方式。这个程序解决了这个问题。

从您的问题看起来好像您希望数据按照第一个字段的顺序排序,因此我已将所有文件读入内存并将相应的文件打印出来。我还使用了autodie来避免为所有IO操作编写状态检查。

use strict;
use warnings;
use autodie;

use Text::CSV;

my $csv = Text::CSV->new({ sep_char => ';', eol => $/ });

my @data;

while ( my $row = $csv->getline(\*DATA) ) {
  push @data, $row;
}

my ($file, $fh);

for my $row ( sort { $a->[2] <=> $b->[2] or $a->[0] <=> $b->[0] } @data ) {
  unless (defined $file and $file == $row->[2]) {
    $file = $row->[2];
    open $fh, '>', "$file.csv";
  }
  $csv->print($fh, $row);
}

close $fh;

__DATA__
10002394;22.98;48;http://testdata.com/bla/29012827.jpg;5.95;93962094820
10025155;27.99;65;http://testdata.com/bla/29011075.jpg;5.95;14201021349
10003062;19.99;26;http://testdata.com/bla/29002816.jpg;5.95;17012725049
10003122;13.0;53;http://testdata.com/bla/29019899.jpg;5.95;24404000059
10029650;27.99;48;http://testdata.com/bla/29003007.jpg;5.95;3692164452
10007645;20.99;65;http://testdata.com/bla/28798580.jpg;5.95;10201848233    
10025825;12.99;65;http://testdata.com/bla/29017837.jpg;5.95;93962025367