電子書籍の厳選無料作品が豊富!

あるテキストファイルが以下の内容で記述されているとします。
a   file1
a   file1
a   file1
b   file1
b   file1
b   file1
c   file2
c   file2
d   file2
d   file2
e   file2
e   file2
f   file3
f   file3
f   file3

上記のように列が二つあるテキストファイルについて、
2列目のfile名が1列目のどの値とひもづいて
いるかを処理するperlプログラムを作ることを考えます。
出来上がったperlによって上記テキストファイルを処理した結果は、
file1は aとb
file2は cとdとe
file3は f
とひもづいていることが分かる ということにしたいです。
これをperlプログラムで書くとき、条件として
一行一行を読みとるとき
if ( $_ =~ /(\S+)\s+(\S+)/ ){
を使っています。
そのため$1と$2に現在行の1列目,2列目が与えられた後、
ハッシュと配列を組み合わせて考えた場合どのようにすれば
いいのでしょうか。

ご助言ご鞭撻宜しくお願い致します。

A 回答 (3件)

> file名が一度変わり、さらに変わるときに既に出てきたfile名はエラーとする です。



同じfile名の行はひとかたまりに成っていなくてはダメということでしょうか?
であるなら、生成する部分で「直前のfile名と違い、かつ、既に出現済のfile名を取得した」場合にエラーにすれば良いでしょう。

例) ----------------------------------------------

my $previousFile = '';
while (<FILE>) {
if ($_ =~ /(\S+)\s+(\S+)/) {
if ($previousFile and $2 ne $previousFile and exists($data{$2})) {
die "$2は既に出てきたためエラーとして処理をストップしました";
}
$data{$2}{$1} = 1;
$previousFile = $2;
}
}

--------------------------------------------------

> これをループ文(whileやforeach)などで取り出す場合の記述例を御教授願います。

%result のキーでループさせてその内側で値の配列をループさせれば全要素を走査できます。file名順に並べなくても良いなら while と each の組合せでも大丈夫です。

例) ----------------------------------------------

foreach my $file (sort(keys(%result))) {
print "* $file\n";
foreach my $str (@{$result{$file}}) {
print " - $str\n";
}
}

--------------------------------------------------

単に配列が値になっているハッシュというだけの話なので、perlの制御構文やハッシュの使い方を調べれば、走査のやり方は分かるでしょう。
http://rfs.jp/sb/perl/02/06.html
http://rfs.jp/sb/perl/02/05.html



なお、ベストアンサーはどうでも良いですが、マルチポストは止めましょう。いちおう、このサイトのルールですし、質問する方も回答する方も面倒なだけでしょう。

この回答への補足

マルチポストの件了解しました。
返信遅くなってしまい大変失礼致しました。
助かりました。ありがとうございました。

補足日時:2013/02/04 12:53
    • good
    • 0

> $data{$2} = 値 のようにキーは$2のみにして$1は値として



一度ハッシュのハッシュを作った上で、それから配列のハッシュを作るのが簡単でしょう。

例) ----------------------------------------------

my %data = ();

open(FILE, 'hoge.txt') or die "file open failed.";

while(<FILE>) {
if ( $_ =~ /(\S+)\s+(\S+)/ ) {
$data{$2}{$1} = 1;
}
}

close(FILE);


# 配列のハッシュを作る

my %result = ();

while(my ($key, $val) = each(%data)) {
$result{$key} = [sort(keys(%$val))];
}


# 生成したデータ表示してみる。

foreach my $file (sort(keys(%result))) {
print $file . " -> " . join(',', @{$result{$file}}) . "\n";
}


use Data::Dumper;
print Dumper(\%result);

--------------------------------------------------

1ループでも出来ない事はないですが、かえって面倒だと思いますよ。


> また出力はただのprintでお願いできますか。

えーと、データ表示の部分に関しては、実行した際にデータがどの様に格納されているのか確認できるように付けただけですので、コード自体の意味が分からなかったら無視して頂いても大丈夫です。

この回答への補足

root139様

御回答ありがとうございます。
すでにベストアンサーに決定することを決めています。
しかし、もう2点の課題について御教授いただければと思います。
(この課題がわからなくて答えられなくてもベストアンサーにするのでご安心を)

1点目です。
テキストファイルの続きが存在し、
g  file1
g  file1
g  file1
のような続き行があるとします。
file1は既に出てきた値としてエラー文
「file1は既に出てきたためエラーとして処理をストップしました」
を出力させたいのです。
エラーの条件は、file列でfile1→file2→file1のように
file名が一度変わり、さらに変わるときに既に出てきたfile名
はエラーとする です。
具体的に言いますと新しい行をテキストファイルに追加する場合、
2列目は file1 file2 以外のfile名でなくてはなりません(file3は許されます)

2点目
while(my ($key, $val) = each(%data)) {
 $result{$key} = [sort(keys(%$val))];
}
の%resultに格納された配列を実際に取り出したい
場合の取り出し方のバリエーションを記述お願いできますでしょうか。
$result{$key}[0]
で1列目の1個目の要素を取り出せそうに思えますが、
これをループ文(whileやforeach)などで
取り出す場合の記述例を御教授願います。

長々となってしまい申し訳ございませんが、宜しくお願い致します。

補足日時:2013/01/28 12:52
    • good
    • 0

やり方はいろいろ有ると思いますが、ハッシュのハッシュを使うのが簡単だろうと思います。



例) ----------------------------------------------

my %data = ();

open(FILE, 'hoge.txt') or die "file open error!.";

while(<FILE>) {
if ( $_ =~ /(\S+)\s+(\S+)/ ) {
$data{$2}{$1} = 1;
}
}

close(FILE);


# 生成したデータ表示してみる。

foreach my $file (sort(keys(%data))) {
printf("%s -> %s\n", $file, join(',', sort(keys(%{$data{$file}}))));
}


use Data::Dumper;
print Dumper(\%data);

--------------------------------------------------

生成しているデータは、最後の Data::Dumper の出力を見てもらえば分かりますが、1列目の[a, b, c]がキーで値が1のハッシュを2列目のfile名がキーのハッシュに格納しています。

この回答への補足

ご回答ありがとうございます。
また補足ですみません。
ハッシュの値は1ではなく、
$data{$2} = 値
のようにキーは$2のみにして$1は値として
格納することを考えてますので、そういった
場合どう記述すればいいのか宜しくお願い致します。

また出力はただのprintでお願いできますか。
自分まだまだ初心者なものでして、
printfやdumperがわかりません。
普通のprintならわかります。

宜しくお願い致します。

補足日時:2013/01/27 14:04
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!