- ベストアンサー
配列の作業
perlを勉強し始めて一月足らずのものです。 どう考えてプログラムを組めばいいのかわからなく。 ちょっと困っています。 #!usr/local/bin/perl $acdata{'id'} = 125; $acdata{'go'} = 'http://www55.'; if(open(IN,"test5.log")){ @log = <IN>; close IN; }else{ $point =1; $log = join ('<>',$acdata{'id'},$point,$acdata{'go'}); open (OUT,">test5.log"); print OUT "$log\n"; close OUT; } foreach (@log){ ($id,$point,$url) = split (/<>/,$_); if ($id == $acdata{'id'}){ $point++; $log = join ('<>',$acdata{'id'},$point,$acdata{'go'}); open (OUT,">test5.log"); print OUT $log; } ログファイルの中のIDナンバーで管理するのですが ファイル自体がなかったらファイルを作って新規データーを書き込む IDナンバーと%acdata{'id'}が一致したらポイントを加算して記憶させる。 そこまではできたのですが、すべてのIDとと%acdata{'id'}が 合わなかった場合、データーを追加で書き込むという作業が なかなか思うように行かず、何度となく書いては消してを繰り返しています。 foreachの中でコントロールできないのかなと思ったりもしましたが どんな風に考えたらいいのでしょう? よかったらご指導ください。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
#1です。 一つミスしてました。フラグが変更していなかったら、ログ配列に追加するでした。 #!usr/local/bin/perl $acdata{'id'} = 11; $acdata{'go'} = "hhh"; $logfile = "test5.log"; if(-T $logfile){ #ログファイルがない場合 @log = (); }else{ #ログファイルがある場合 open(IN,$logfile); @log = <IN>; close IN; } #フラグはループする前に立てる $flag = 0; foreach(@log){ ($id,$point,$url) = split (/<>/,$_); if($acdata{'id'} == $id){ #IDが一致した場合$_(一致したデータ)を書き換え、フラグを変更 $_ = join('<>',$acdata{'id'},++$point,$acdata{'go'}."\n"); $flag = 1; } } if(flag == 0){ #フラグが変更されていない(一致するデータがない)場合、ログ配列に新規データを追加 push @log,join('<>',$acdata{'id'},1,$acdata{'go'}."\n"); } open(LOG,">$logfile"); print LOG @log; close LOG;
その他の回答 (3)
- osamuy
- ベストアンサー率42% (1231/2878)
データにアクセスするのにキーを用いていて、そのデータが存在するか否かに重きをおくなら、単純なテキスト形式でなく、ハッシュデータベース形式のデータファイルを使ってみては。こんな感じ: dbmopen( %DB, 'acdata', 0644 ) or die; sub get { my( $id ) = @_; my( $point, $go ) = split( $;, $DB{ $id } ); $point = 0 unless $point; return( 'id', $id, 'point', $point, 'go', $go ); } sub addpoint { my( $id, $point, $go ) = @_; my %a = &get( $id ); $point += $a{'point'}; $DB{ $id } = join( $;, ($point, $go) ); return( 'id', $id, 'point', $point, 'go', $go ); } sub top10 { print "Top 10:\n"; my @order; foreach my $id ( keys %DB ){ my $a = &get( $id ); push @order, [ $id, $a{'point'} ]; } my $rank = 0; foreach my $id ( map { $_->[0] } sort { $b->[1] <=> $a->[1] } @order ){ $rank++; last if $rank > 10; my %a = &get( $id ); print '[', $rank, ']ID=', $id, ',Point=', $a{'point'}, ',Go=', $a{'go'}, "\n"; } } %ac = &addpoint( '125', 10, 'http://www55' ); &top10(); %ac = &addpoint( '125', 10, 'http://www55' ); &top10(); %ac = &addpoint( '11', 10, 'http://goo' ); &top10(); %ac = &addpoint( '12', 10, 'http://goo' ); &top10(); %ac = &addpoint( '11', 10, 'http://goo' ); &top10(); dbmclose( %DB ); 以下が実行結果: Top 10: [1]ID=125,Point=10,Go=http://www55 Top 10: [1]ID=125,Point=20,Go=http://www55 Top 10: [1]ID=125,Point=20,Go=http://www55 [2]ID=11,Point=10,Go=http://goo Top 10: [1]ID=125,Point=20,Go=http://www55 [2]ID=11,Point=10,Go=http://goo [3]ID=12,Point=10,Go=http://goo Top 10: [1]ID=125,Point=20,Go=http://www55 [2]ID=11,Point=20,Go=http://goo [3]ID=12,Point=10,Go=http://goo
お礼
ハッシュを使って簡単なデーターベースもできるんですね。今、リファレンス本を見ながらちょっと拝見しましたが、これもじっくり拝見してちょっと試してみたいと思います。参考URLもありがとうございました。
- k_o_r_o_c_h_a_n
- ベストアンサー率55% (526/942)
どうせ全行取込、全行書き戻しするのだから、整理整頓しながら処理するように考えれば わかりやすいと思いますが.. #!usr/local/bin/perl $acdata{'id'} = 125; $acdata{'go'} = 'http://www55.'; #取込 open(IN,"test5.log"); while(<IN>) { @l=split(/<>/,3); $POINT{$l[0]}=$l[1]; $URL{$l[0]}=$l[1]; } close(IN); #加算 if($POINT{$acdata{'id'}} eq "") { #無いじゃん $POINT{$acdata{'id'}}=1; $URL{$acdata{'id'}}=$acdata{'go'}; } else { #あった $POINT{$acdata{'id'}}++; } #書き戻し open(OUT,"test5.log"); for(sort(keys(%POINT)) { print OUT $_,'<>',$POINT{$_},'<>',$URL{$_},"\n"; } close(OUT); 未テストです。
お礼
お答えありがとうございます。 少し時間がなかったので昨晩御礼をかけなくてごめんなさい。 ソースを見て「あぁ~こんな風な考え方もOKなんだ」と勉強を始めた私としてはとても参考になりました。それぞれの値をハッシュとして取り出してしまうというのもすごいですね。ソースの方を保存したのでもう少し、ソースを見て別パターンのやり方としてやってみます。本当にありがとうございました。
- trisagion
- ベストアンサー率68% (15/22)
まずフラグを立て($flag = 0)、foreach内でデータの一致するものがあればフラグを変更($flag = 1)する。(一致するデータがあればそのデータも変更) ループ終了後、フラグが変更していればログ配列に追加し、その後ファイルに書き出す。 以上の様な方法はどうでしょう? $flag = 0; foreach $log(@log){ ($id,$point,$url) = split(/<>/,$_); if($id == $acdata{'id'}){ $log = join('<>',$acdata{'id'},++$point,$acdata{'go'}); flag = 1; } } if(flag == 1){ push @log,join('<>',$acdata{'id'},'0',$acdata{'go'}); } open(LOG,">log.txt"); foreach(@log){ print LOG "$_\n"; } close LOG;
補足
早速ありがとうございました。 こういうことかなぁ~ということで自分のソースに組み込んで見ました。 #!usr/local/bin/perl $acdata{'id'} = 11; $acdata{'go'} = "hhh"; if (open (IN,"test.log")){ @log = <IN>; chomp(@log); close IN; } else{ open (OUT,">test.log"); $point = 1; $log = join ('<>',$acdata{'id'},$point, $acdata{'go'}); print OUT "$log\n"; } foreach (@log){ ($id,$point,$jumpurl) = split(/<>/,$_); $flog = 0; if ( $id == $acdata{'id'}){ $point++; $flog = 1; } } if ($flog == 1){ push @log, join ('<>',$acdata{'id'},$point,$acdata{'go'}); } open (OUT,">>test.log"); foreach(@log){ print OUT "$_\n"; } close OUT; exit; しかし、一番肝心な部分がうまく作動しないようです。 ファイルログに記録してあるIDと取り込んだIDが一緒の場合、そのままポイントの数値をインクリメントした形で再度記録をするような形にしたいのですが、ポイントがインクリメントされていないログとインクリメントされたログの両方がファイルに記録されてしまうようです。 私の説明が下手なのでご理解していただけるかわからないのですが、入力されたIDとログのIDが一致した場合、IDが一致した配列を消去してポイントをインクリメントした同じIDの配列を差し替えたいということなんですが、foreachの処理の中で何度か色々考えてやってみたのですが、僕のか考えていることは無理な話なのかなと自信がなくなったので皆さんにお聞きしたいと思って質問しました。 また、配列の中に合致するIDがなかった場合新たに取得したIDナンバーにポイント1をセットしてURLとともにファイルに書き込む作業もしなければなりませんが、その辺をどうしたらいいのか御教示いただけたらなと思います。初心者の説明ですからわかりにくかったら申し訳ありません。
お礼
色々とご指導ありがとうございました。 教えていただいたことをソースに反映させたところ、どうにか自分が思ったとおりの動作をしてくれるようになりました。何しろ、始めたばっかりなのでどういう考え方でやればいいのかそういった部分で経験不足は否めないものか、すごく参考になりました。こういったサイトや皆さんの暖かい助言に感謝します。 また何かすぐお世話になることもあるかと思いますが、ご助言いただけたら幸いです。