こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

定石? 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 :

投稿日時 - 2009-05-04 11:24:20

QNo.4930064

暇なときに回答ください

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

> 逆に入力ファイルのフォーマットを処理しやすいものにしてしまっても構いません

じゃあ
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
------------------------

ただこのプログラムの問題は、見ての通りカテゴリのアルファベット順になってしまうことです。
元のファイルに書かれた順番通りにしなければならないのなら
プログラムを変更しなければなりません。

投稿日時 - 2009-05-04 14:44:45

お礼

有難うございます。
アルファベット順であることは特に構いません。

僕が愚かで、最初のキーを「見つけるまで」などを
my $flag_first_match = 1;
my $flag_hit = 0;
$flag_seek などとしていたのですが、definedを使えばすっきり処理できる($flagもいらない)ことは大変参考になりました。


my $category;
...
if (/^-\s*(\S+)/) {
$category = $1;
} else {
if (defined $category) {
...

どうも有難うございます。

投稿日時 - 2009-05-04 16:37:14

このQ&Aは役に立ちましたか?

7人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.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

投稿日時 - 2009-05-08 01:40:29

あなたにオススメの質問