人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

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

●質問者: ishikennn
●カテゴリ:コンピュータ
✍キーワード:A1 Perl いるか カウント コンマ
○ 状態 :終了
└ 回答数 : 4/4件

▽最新の回答へ

1 ● tombe
●30ポイント

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とかがあってもうまく計算できますね.


2 ● thrillseeker
●30ポイント

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

#! /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っていうエラーメッセージが出るんですが,何か問題があるのでしょうか?


3 ● thrillseeker
●0ポイント

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

◎質問者からの返答

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


4 ● lunlumo
●30ポイント ベストアンサー

このフォーマットであればtombeさんのやり方で十分な気もしますが,CSVファイルの解析として取り組むならこんな感じでしょうか。

#! /usr/bin/perl

packageObject;

usestrict;
usebase qw(Class::Accessor);

sub new {
my($pkg) = @_;
bless({},$pkg);
}


packageCompany;

usestrict;
usebase 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;
}


packageCompanyIO;

usestrict;
useText::CSV;
useIO::File;
usebase 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();
}


packageCompanyReader;

usestrict;
useText::CSV;
usebase 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;
}


packageCompanyWriter;

usestrict;
useText::CSV;
usebase 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");
}
}


packagemain;

usestrict;

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を使う方法ですね。圧巻です。

ありがとうございます。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ