Perlでの処理について質問です.


あるファイルに,
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までのコンマの数をカウントさせたりすればいいのかなと思い,色々試したんですけど全然うまくいかないので,力を貸していただけたらなと思います.

回答の条件
  • URL必須
  • 1人3回まで
  • 登録:
  • 終了:2007/12/04 06:10:25
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:lunlumo No.4

回答回数107ベストアンサー獲得回数14

ポイント30pt

このフォーマットであれば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

id:ishikennn

Text::CSVを使う方法ですね。圧巻です。

ありがとうございます。

2007/12/04 06:09:07

その他の回答3件)

id:tombe No.1

回答回数38ベストアンサー獲得回数7

ポイント30pt

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,

id:ishikennn

ありがとうございます!正常に動きました.

このコードだとcompany以外に例えばhospital1とかuniversity3とかがあってもうまく計算できますね.

2007/12/02 23:07:53
id:thrillseeker No.2

回答回数328ベストアンサー獲得回数37

ポイント30pt

こんにちは。以下でどうですか?

#! /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

id:ishikennn

ありがとうございます.

panic: utf16_to_utf8: odd bytelenっていうエラーメッセージが出るんですが,何か問題があるのでしょうか?

2007/12/02 23:00:11
id:thrillseeker No.3

回答回数328ベストアンサー獲得回数37

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";
}

http://perldoc.jp/docs/perl/5.6.1/perldiag.pod

id:ishikennn

うまく動きました.ものすごくすっきりしたプログラムですね!すごいです.

2007/12/02 23:37:30
id:lunlumo No.4

回答回数107ベストアンサー獲得回数14ここでベストアンサー

ポイント30pt

このフォーマットであれば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

id:ishikennn

Text::CSVを使う方法ですね。圧巻です。

ありがとうございます。

2007/12/04 06:09:07

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません