あるファイルに,
company1=2,4,a2,5,9,10,7,11,
company1=5,3,4,12,2,18,2,12,
company1=7,4,a6,11,5,2,6,4,
company1=6,2,a9,34,4,2,7,2,
company1=4,7,2,6,6,9,a2,7,
company2=4,9,a12,8,1,7,a1,5,
company2=3,5,5,3,1,9,2,1,
company2=a11,4,2,5,3,1,a12,11,
company3=....
(こういった感じでcompany10まで続いています)
というデータが入っています.
この場合,companyごとに,一番初めのaが数字の前についている入っているデータまでには何個データが入っているかも出力させ,また,aが数字の前についている入っているデータとその次のが数字の前についている入っているデータまでに何個データが入っているかを出力させたいです.(説明が下手ですみません)
例えば上のような場合では,
company1でa2が出てくるまでには2つデータがあり,そのa2と次のa6の間には15個,a6と次のa9の間には7個...といった感じになっているので,
company1→2,15,7,11,
company2→2,3,9,5,
という風に出力させたいです.
aからaまでのコンマの数をカウントさせたりすればいいのかなと思い,色々試したんですけど全然うまくいかないので,力を貸していただけたらなと思います.
このフォーマットであればtombeさんのやり方で十分な気もしますが,CSVファイルの解析として取り組むならこんな感じでしょうか。
#! /usr/bin/perl package Object; use strict; use base qw(Class::Accessor); sub new { my ($pkg) = @_; bless({},$pkg); } package Company; use strict; use base qw(Object); __PACKAGE__->mk_accessors(qw(name data parsed)); sub initialize { my ($self) = @_; $self->data([]); $self->parsed([]); $self; } sub parse { my ($self) = @_; my $i = 0; my @parsed = (); foreach my $data (@{$self->data()}) { if ($data !~ m/^a/) { $i++; } else { push(@parsed,$i); $i = 0; } } $self->parsed(\@parsed); $self; } package CompanyIO; use strict; use Text::CSV; use IO::File; use base qw(Object); __PACKAGE__->mk_accessors(qw(type file_name file csv)); sub initialize { my ($self) = @_; my $file = new IO::File(); my $csv = new Text::CSV({'binary'=>1}); $file->open($self->file_name(),$self->type()) || die "cannot open file"; $self->file($file); $self->csv($csv); $self; } sub finalize { my ($self) = @_; $self->file()->close(); } package CompanyReader; use strict; use Text::CSV; use base qw(CompanyIO); __PACKAGE__->mk_accessors(qw(companies)); sub initialize { my ($self) = @_; $self->type('r'); $self->SUPER::initialize(); } sub read { my ($self) = @_; my $csv = $self->csv(); my $file = $self->file(); my %cache = (); my @companies = (); while (1) { my $company; my $data; my ($name,$value); my $columns = $csv->getline($file); last if ($csv->eof()); die $csv->error_diag()."(".$csv->error_input().")\r\n" unless ($csv->status()); ($name,$value) = split(/=/,shift(@$columns),2); if (defined($cache{$name})) { $company = $cache{$name}; } else { $company = new Company(); $company->name($name); $company->initialize(); $cache{$name} = $company; } $data = $company->data(); push(@$data,$value,@$columns); $#$data-- if ($data->[$#$data] eq ''); $company->data($data); } @companies = values(%cache); $_->parse() foreach (@companies); $self->companies(\@companies); $self; } package CompanyWriter; use strict; use Text::CSV; use base qw(CompanyIO); __PACKAGE__->mk_accessors(qw(companies)); sub initialize { my ($self) = @_; $self->type('w'); $self->SUPER::initialize(); } sub write { my ($self) = @_; my $file = $self->file(); my $csv = $self->csv(); foreach my $company (sort { $a->name() cmp $b->name() } @{$self->companies()}) { $csv->combine($company->name(),@{$company->parsed()}) || die "invalid data."; $file->print($csv->string()."\r\n"); } } package main; use strict; my $reader = new CompanyReader(); my $writer = new CompanyWriter(); if (scalar(@ARGV) != 2) { print "usage: ${0} IN_FILE OUT_FILE\r\n"; exit; } $reader->file_name($ARGV[0]); $reader->initialize(); $reader->read(); $writer->file_name($ARGV[1]); $writer->initialize(); $writer->companies($reader->companies()); $writer->write(); $reader->finalize(); $writer->finalize(); 1;
http://search.cpan.org/~makamaka/Text-CSV-1.00/lib/Text/CSV.pm
http://www2u.biglobe.ne.jp/~MAS/perl/ref/split.html
URLはダミーです。
あまりスマートではありませんがこんな感じ?
注)インデントに全角スペースを使ってます。
%table = ();
# まずデータをcompanyごとに連結してしまう。
while (<DATA>)
{
chomp();
next unless /^(.*?)=(.*)/;
$table{$1} .= $2;
$table{$1} =~ s/,*$/,/; # 最後のカンマがあるかどうか疑わしいので改めて付け直す
}
# 各companyの集計
for (keys %table)
{
print "$_→";
$table{$_} =~ s/(a\d+)[^a]*$/$1/; # 最後の a数字 以降のデータを捨てる
@clms = split(/a\d+/,$table{$_}); # a数字 で分割する
for (@clms) # @clmsの各要素は、a数字を除いたデータが入っている
{
s/^,//; # 頭のカンマ削除
s/,$//; # 最後のカンマ削除
print (s/,//g + 1); # カンマの数を数えると植木算なので+1する
print ',';
}
print "\n";
}
__END__
company1=2,4,a2,5,9,10,7,11,
company1=5,3,4,12,2,18,2,12,
company1=7,4,a6,11,5,2,6,4,
company1=6,2,a9,34,4,2,7,2,
company1=4,7,2,6,6,9,a2,7,
company2=4,9,a12,8,1,7,a1,5,
company2=3,5,5,3,1,9,2,1,
company2=a11,4,2,5,3,1,a12,11,
ありがとうございます!正常に動きました.
このコードだとcompany以外に例えばhospital1とかuniversity3とかがあってもうまく計算できますね.
こんにちは。以下でどうですか?
#! /usr/bin/perl while(<>) #引数で与えられたファイルを読み込み1行ずつ処理する { if (/^(company[¥d]+)=(.*)/) #company名を$1,中身を$2として取得 { $data{$1} .= $2; #company名をkeyとする連想配列に中身を追加 } } foreach $company (sort(keys(%data))) #company名をkeyとする連想配列をkeyごとに処理 { my @subdata = split(/a[¥d]+[,]*/,$data{$company}); #(a数字..)で中身を分割 print "$company="; foreach $i (0..$#subdata-1) #最後の要素を除いた分割要素を一つずつ処理 { $subdata[$i] =~ s/,$//; #末尾の,を削除 my @subsubdata = split(",",$subdata[$i]); #要素を,で分割 print $#subsubdata+1,","; #分割して出来た新しい要素の要素数を出力 } print "¥n"; }
Perl のハッシュ(連想配列)について: http://www.site-cooler.com/kwl/perl/4.htm
ありがとうございます.
panic: utf16_to_utf8: odd bytelenっていうエラーメッセージが出るんですが,何か問題があるのでしょうか?
panic: utf16_to_utf8: odd bytelenっていうエラーメッセージが出るんですが,何か問題があるのでしょうか?
コメントの日本語の処理の問題かもしれません。
ソースからコメントを削除したのでこちらをお試しください。
#! /usr/bin/perl while(<>) { if (/^(company[¥d]+)=(.*)/) { $data{$1} .= $2; } } foreach $company (sort(keys(%data))) { my @subdata = split(/a[¥d]+[,]*/,$data{$company}); print "$company="; foreach $i (0..$#subdata-1) { $subdata[$i] =~ s/,$//; my @subsubdata = split(",",$subdata[$i]); print $#subsubdata+1,","; } print "¥n"; }
うまく動きました.ものすごくすっきりしたプログラムですね!すごいです.
このフォーマットであればtombeさんのやり方で十分な気もしますが,CSVファイルの解析として取り組むならこんな感じでしょうか。
#! /usr/bin/perl package Object; use strict; use base qw(Class::Accessor); sub new { my ($pkg) = @_; bless({},$pkg); } package Company; use strict; use base qw(Object); __PACKAGE__->mk_accessors(qw(name data parsed)); sub initialize { my ($self) = @_; $self->data([]); $self->parsed([]); $self; } sub parse { my ($self) = @_; my $i = 0; my @parsed = (); foreach my $data (@{$self->data()}) { if ($data !~ m/^a/) { $i++; } else { push(@parsed,$i); $i = 0; } } $self->parsed(\@parsed); $self; } package CompanyIO; use strict; use Text::CSV; use IO::File; use base qw(Object); __PACKAGE__->mk_accessors(qw(type file_name file csv)); sub initialize { my ($self) = @_; my $file = new IO::File(); my $csv = new Text::CSV({'binary'=>1}); $file->open($self->file_name(),$self->type()) || die "cannot open file"; $self->file($file); $self->csv($csv); $self; } sub finalize { my ($self) = @_; $self->file()->close(); } package CompanyReader; use strict; use Text::CSV; use base qw(CompanyIO); __PACKAGE__->mk_accessors(qw(companies)); sub initialize { my ($self) = @_; $self->type('r'); $self->SUPER::initialize(); } sub read { my ($self) = @_; my $csv = $self->csv(); my $file = $self->file(); my %cache = (); my @companies = (); while (1) { my $company; my $data; my ($name,$value); my $columns = $csv->getline($file); last if ($csv->eof()); die $csv->error_diag()."(".$csv->error_input().")\r\n" unless ($csv->status()); ($name,$value) = split(/=/,shift(@$columns),2); if (defined($cache{$name})) { $company = $cache{$name}; } else { $company = new Company(); $company->name($name); $company->initialize(); $cache{$name} = $company; } $data = $company->data(); push(@$data,$value,@$columns); $#$data-- if ($data->[$#$data] eq ''); $company->data($data); } @companies = values(%cache); $_->parse() foreach (@companies); $self->companies(\@companies); $self; } package CompanyWriter; use strict; use Text::CSV; use base qw(CompanyIO); __PACKAGE__->mk_accessors(qw(companies)); sub initialize { my ($self) = @_; $self->type('w'); $self->SUPER::initialize(); } sub write { my ($self) = @_; my $file = $self->file(); my $csv = $self->csv(); foreach my $company (sort { $a->name() cmp $b->name() } @{$self->companies()}) { $csv->combine($company->name(),@{$company->parsed()}) || die "invalid data."; $file->print($csv->string()."\r\n"); } } package main; use strict; my $reader = new CompanyReader(); my $writer = new CompanyWriter(); if (scalar(@ARGV) != 2) { print "usage: ${0} IN_FILE OUT_FILE\r\n"; exit; } $reader->file_name($ARGV[0]); $reader->initialize(); $reader->read(); $writer->file_name($ARGV[1]); $writer->initialize(); $writer->companies($reader->companies()); $writer->write(); $reader->finalize(); $writer->finalize(); 1;
http://search.cpan.org/~makamaka/Text-CSV-1.00/lib/Text/CSV.pm
Text::CSVを使う方法ですね。圧巻です。
ありがとうございます。
Text::CSVを使う方法ですね。圧巻です。
ありがとうございます。