• ベストアンサー

定石? perlで入力ファイルを処理する方法

以下のようなファイルを入力して、配列/連想配列に格納、これを後段の処理に利用したいと考えています。結構複雑なことをやってようやく処理できたのですが、もっと良い方法はありませんでしょうか。 逆に入力ファイルのフォーマットを処理しやすいものにしてしまっても構いません(perl形式で記述してrequireする方法はここではNGとします)。アイディア宜しくお願い致します。 1. #はコメント 2. 最終イメージ  Vegetable: Tomato, Lettuce, Potato Fish : Salmon, Tuna, Fruit : Banana, Orange ※ ####~次の####までを塊として処理する部分で、ちょっと冗長な気がしています --- >8 --- >8 --- >8 --- >8 --- >8 # This is comment line # File created by ... # since 5/May/2009 #### Vegetable Tomato Lettuce Potato # Carrot <-- Comment line #### Fish Salmon Tuna #### Fruit Banana Orange --- >8 --- >8 --- >8 --- >8 --- >8 <<スクリプト例>> # ここでは@keyにカテゴリ(Vegetable, Fish, Fruit)、@valueに各々の食品名(Tomato, lettuce,...)を入れています #! /usr/bin/perl my $flag_first_match = 1; my $flag_hit = 0; my @key; my @value; open (LIST, "aaa.txt") || die "open error"; while(<LIST>){ if($flag_hit == 1){ $flag_hit = 0; $flag_seek = 1; if($flag_first_match==1){ $flag_first_match = 0; } } if(m/####\s+(.*)$/){ $key_tmp = $1; $flag_hit = 1; $flag_seek = 0; } chomp(); if(m/^\s*$/){next;} if(m/^#\s+/){next; } if($flag_hit==1 && $flag_first_match ==0 ){ # for on-going loop push(@value, $ref_tmp); } if($flag_hit == 1){ # for next loop push(@key, $key_tmp); $ref_tmp = []; } if($flag_seek == 1){ push(@$ref_tmp, $_); } } close(LIST); ### 印字確認 { local $, = ", "; for($i=0; $i < @key; $i++){ printf ("%2d | %-35s: ", $i, $key[$i]); foreach $ref ($value[$i]) { for ($j=0; $j < @$ref; $j++){ print "$j= @$ref[$j] "; } } print "\n"; } } <<出力>> 0 | Vegetable : 0= Tomato 1= Lettuce 2= Potato 1 | Fish : 0= Salmon 1= Tuna 2 | Fruit :

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

> 逆に入力ファイルのフォーマットを処理しやすいものにしてしまっても構いません じゃあ 1. #以降はコメント 2. -以降はカテゴリ と割り切ってしまうのはどうでしょう? #以降は何がなんでもコメントで、カテゴリは別の文字(私の場合は-にしました)にしたほうが考えやすいと思います。 ####をコメントに使うとなると、 「先頭に#があった場合はコメントだが、####だった場合は例外」 としなければなりません。 例外はできれば増やしたくないものです。 私のフォーマットに変えたファイルの内容は以下です。 ------------------------ # This is comment line # File created by ... # since 5/May/2009 - Vegetable Tomato Lettuce Potato # Carrot <-- Comment line - Fish Salmon Tuna - Fruit Banana Orange ------------------------ プログラムの内容は以下。 ------------------------ #! /usr/bin/perl use warnings; use strict; my $category; my %pair; open (LIST, "aaa.txt") || die "open error"; while(<DATA>) { chomp; next if /^\s*$/; # blank line next if /^#/; # comment line if (/^-\s*(\S+)/) { $category = $1; } else { if (defined $category) { s/^\s*(.*?)\s*$/$1/; # 前後の空白を取り除く push @{$pair{$category}}, $_; } } } close(LIST); ### 印字確認 my @keys = sort keys %pair; for (my $i = 0; $i < @keys; $i++) { my $key = $keys[$i]; print "$i | $key : " . join(", ", @{$pair{$key}}) . "\n"; } ------------------------ 出力結果は以下です。 ------------------------ 0 | Fish : Salmon, Tuna 1 | Fruit : Banana, Orange 2 | Vegetable : Tomato, Lettuce, Potato ------------------------ ただこのプログラムの問題は、見ての通りカテゴリのアルファベット順になってしまうことです。 元のファイルに書かれた順番通りにしなければならないのなら プログラムを変更しなければなりません。

tk_1980024
質問者

お礼

有難うございます。 アルファベット順であることは特に構いません。 僕が愚かで、最初のキーを「見つけるまで」などを my $flag_first_match = 1; my $flag_hit = 0; $flag_seek などとしていたのですが、definedを使えばすっきり処理できる($flagもいらない)ことは大変参考になりました。 my $category; ... if (/^-\s*(\S+)/) { $category = $1; } else { if (defined $category) { ... どうも有難うございます。

その他の回答 (1)

  • sample_
  • ベストアンサー率76% (20/26)
回答No.2

YAMLモジュールとリファレンスを使ってよければ。 ./file/data.txt ================================================ # File created by... # since 5/May/2009 - category : Vegetable   data :     - Tomato     - Lettuce     - Potato # Carrot - category : Fish   data :     - Salmon     - Tuna - category : Fruit   data :     - Banana     - Orange ================================================ ↑行頭の半角スペースが認識してくれないので代わりに全角スペースをいれました。行頭の全角スペースを半角スペースに直して使っていただきたいです。 ./test.pl ================================================ use strict; use warnings; use YAML(); my $data = YAML::LoadFile("./file/data.txt"); for(my $i=0; $i<@{$data}; $i++){ my $str = ""; for(my $ii=0; $ii<@{$data->[$i]{data}}; $ii++){ $str .= $ii. "=". $data->[$i]{data}[$ii]. " "; } print "$i | $data->[$i]{category} : $str\n"; } ================================================ <<出力>> 0 | Vegetable : 0=Tomato 1=Lettuce 2=Potato 1 | Fish : 0=Salmon 1=Tuna 2 | Fruit : 0=Banana 1=Orange