• ベストアンサー

配列の作業

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の中でコントロールできないのかなと思ったりもしましたが どんな風に考えたらいいのでしょう? よかったらご指導ください。

  • Perl
  • 回答数4
  • ありがとう数4

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

  • ベストアンサー
  • trisagion
  • ベストアンサー率68% (15/22)
回答No.3

#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;

sakuzo117
質問者

お礼

色々とご指導ありがとうございました。 教えていただいたことをソースに反映させたところ、どうにか自分が思ったとおりの動作をしてくれるようになりました。何しろ、始めたばっかりなのでどういう考え方でやればいいのかそういった部分で経験不足は否めないものか、すごく参考になりました。こういったサイトや皆さんの暖かい助言に感謝します。 また何かすぐお世話になることもあるかと思いますが、ご助言いただけたら幸いです。

その他の回答 (3)

  • osamuy
  • ベストアンサー率42% (1231/2878)
回答No.4

データにアクセスするのにキーを用いていて、そのデータが存在するか否かに重きをおくなら、単純なテキスト形式でなく、ハッシュデータベース形式のデータファイルを使ってみては。こんな感じ: 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:
http://perlmonks.thepen.com/perlfunc:dbmopen.html
sakuzo117
質問者

お礼

ハッシュを使って簡単なデーターベースもできるんですね。今、リファレンス本を見ながらちょっと拝見しましたが、これもじっくり拝見してちょっと試してみたいと思います。参考URLもありがとうございました。

回答No.2

どうせ全行取込、全行書き戻しするのだから、整理整頓しながら処理するように考えれば わかりやすいと思いますが.. #!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); 未テストです。

sakuzo117
質問者

お礼

お答えありがとうございます。 少し時間がなかったので昨晩御礼をかけなくてごめんなさい。 ソースを見て「あぁ~こんな風な考え方もOKなんだ」と勉強を始めた私としてはとても参考になりました。それぞれの値をハッシュとして取り出してしまうというのもすごいですね。ソースの方を保存したのでもう少し、ソースを見て別パターンのやり方としてやってみます。本当にありがとうございました。

  • trisagion
  • ベストアンサー率68% (15/22)
回答No.1

まずフラグを立て($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;

sakuzo117
質問者

補足

早速ありがとうございました。 こういうことかなぁ~ということで自分のソースに組み込んで見ました。 #!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とともにファイルに書き込む作業もしなければなりませんが、その辺をどうしたらいいのか御教示いただけたらなと思います。初心者の説明ですからわかりにくかったら申し訳ありません。

関連するQ&A

  • 配列の値の操作について教えてください

    num.datの内容が 100 200 300 400 500 の時、以下を実行すると open(IN, "./num.dat"); @number = <IN>; close (IN); $number[0] = $number[0] - 10; open(OUT, ">./num.dat"); print OUT @number; close(OUT); 書き出されたnum.datの内容は 90200 300 400 500 のようになってしまいます。これを 90 200 300 400 500 にしたいのですがどうすればよろしいのでしょうか?くだらない質問ですが、何卒ご教授の程よろしくお願い致します。

    • ベストアンサー
    • Perl
  • ログの一部を消す Perl

    ログの一部を消すPerlを考えています。 ここではソースを短くするために、ログの一部を消し去って ログファイルに上書きするデータは $deta だけだとすると open F, '+<a.log'; flock F,2; $deta = <F>;//実際はここでもっと処理 truncate F,0; seek F,0,0; print F $deta; close F; と、 open IN, 'a.log'; flock IN,2; open OUT, '>a.log'; flock OUT,2; $deta = <IN>;//実際はここでもっと処理 print OUT $deta; close OUT; close IN; の2つを考えました。 上のように 読み込み と 書き込み を同時にやった方がいいのか 下のように分けてやった方がいいのか教えてください。 ロックは flock を使って、普通レベルのロックが できるぐらいでいいと思っているんだけど、 この flock の書き方でおかしいところがあれば 教えてください。

    • ベストアンサー
    • CGI
  • 1ファイルずつ読み込みたい

    ウィンドウズ環境でactive perlを利用しています。 あるディレクトリーの中に2000位のファイルが入っており、このファイルを読み込んで置換処理を行うため下記のようなperlを作っています。 1ファイルはおおよそ3000行くらいです。 foreach(<*.html>) { open(IN_FILE, $_) || die; open(OUT, ">$_.tmp") || die; @record=<IN_FILE>; foreach $record (@record){ $record =~s/aaa/bbb/isg; } print OUT @record; close( IN_FILE); close( OUT); rename("$_.tmp", $_) ; } 当たり前かもわかりませんが、メモリー不足になり処理が中断します。 ディレクトリーの中のファイル数を600程度に少なくすると処理が完了します。 そこで質問なのですが、ディレクトリーの中のファィルを一度に全部読み込まず、1ファイルずつ順次読み込んでいくことはできないでしょうか。 最初の1行目のforeachをwhileに変えたりしたのですがうまくいきません。 対応策あればご教示ください。

    • ベストアンサー
    • Perl
  • ログファイルの指定行に書込み

    open(IN,"$log") || &error("Open Error"); @data = <IN>; close(IN); while (100 <= @data) { pop(@data); } open(OUT,">$log") || &error("Write Error"); print OUT "$in{'id'}<>$in{'comment'}\n"; print OUT @data; close(OUT); ログにはID、時間、コメントが登録されています。 送信データの中に、ログに登録済みのIDがある場合には、そのIDのある行のみを書き換えたいのですが、方法がどうしてもわかりません。 $logに記録されるIDの順番は以下のようにランダムです。IDに登録される文字列は1からの数値のみです。 52<>コメント 120<>コメント 35<>コメント 8<>コメント 2<>コメント 19<>コメント 85<>コメント よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • perlCGIが動かなくないました。改良していただけないでしょうか?

    下記のようなperl-cgiをプロバイダのサーバで使っていたのですが、最近、動かなくなりました。 多分、読み込むファイルの大きさが大きすぎて、途中で止まっているのではないかと思います。 (試しに、10M程度のファイルで行うと、問題なく動きました) 因みに、現在のサイズは、50MBくらいです。 これからも、肥大の一途とたどるのですが、なるべく、サーバに負担をかけず同じファイルを作成できないものでしょうか? とても、困っています。 どなたか、改良できる方、何卒、宜しくお願い致します。 #下記は、INfilenemeから、重複する行を削除して、Outfilenameというファイルを作るperlコードです。 open(IN, "INfilename.txt"); @array = <IN>; close (IN); %count; @array = grep(!$count{$_}++, @array); open(OUT, ">Outfilename.txt"); print OUT join("",@array); close (OUT);

    • ベストアンサー
    • CGI
  • 配列の一部を書き換えてファイルに保存する方法は?

    $dicname ='GermJ.txt'; open(IN, "$dicname"); @list = <IN>; close(IN); 読み込んだ配列@listの5番目のデータを'aaaaaaaaaa'に書き換えたいのですが、 $list[5]='aaaaaaaaaaaaaaaaa'; open(OUT, "> $dicname"); foreach (@list) { print OUT $_; } close(OUT); とやって、ファイルの中身を調べると、6番目の文字列データが5番目の文字列データの後ろにくっついてしまっています。 改行マークを $list[5]='aaaaaaaaaaaaaaaaa',\n;のように行末に追加しても、次の行とのあいだに空間が生まれます。基本的なファイル書き込みの知識をお尋ねして申し訳ありませんが,どうかお教えください。

    • ベストアンサー
    • Perl
  • 連続投票の制限

    投票cgiを設置したいのですがうまくいきません、集計を1日一回だったのを少し改造して即時集計にしました 一応動くのですが、連続投票できてしまいます。 連続投票を一定時間制限できるようにしたいのですがうまくいきません 60秒くらい間をおいてから投票できるようにできますか? ip制限もあるみたいなのですがこれも機能してないです・・・ 改善できるなら元のソースがかなり変わってもかまいません 連続投票制限だけでも機能できればと思っています。よろしくお願いします local $times = time(); sub vote{ if($FORM{id}){ my $fl=0; if(!$CK_ref){$fl=1;} else{ if(index($ENV{'HTTP_REFERER'},index.html>=0){$fl=1;} } if($fl){ $FORM{id}=~s/\n//g; my $vote = $FORM{vt2}?1:0; open(OUT,">>vote_temp.cgi"); #時間用のログファイルに書き込み print OUT "$FORM{id}<>$ENV{'REMOTE_ADDR'}<>$FORM{vt}<>$vote<>".$times."<>\n"; close(OUT); &reset_vote if $VT_RESET < $times; my(@log_lines,%pt,%pt2,%cnt,$name,$value,%CKIP); open(IN,"vote_temp.cgi"); my @log = <IN>; close(IN); if(@log){ my $cktime = $times - 60; #60秒制限 open(IN,"vote_ck_IP.cgi"); #投票した人のIPを記録したログ while(<IN>){ my @ck = split('<>'); next if $_[1] < $cktime; $CKIP{$_[0]} = $_[1]; } close(IN); foreach(@log){ chop; my @rank = split('<>'); #ID<>IP<>評価<>おすすめ<>時間\n next if $CKIP{"$rank[0]_$rank[1]"}; $pt{$rank[0].'_'.$rank[2]}++; $pt2{$rank[0]}++ if $rank[3]; $cnt{$rank[0]}++; $CKIP{"$rank[0]_$rank[1]"} = $rank[4]; } open(OUT,">vote_temp.cgi"); close(OUT); open(OUT,">vote_ck_IP.cgi"); while(($name, $value) = each(%CKIP)){ print OUT "$name<>$value<>\n"; } close(OUT); open(IN,"log.cgi"); my @data = <IN>; close(IN); foreach(@data){ #集計処理長かったので省略 } open(OUT,">vote_bf.cgi"); open(IN,"vote_log.cgi"); while(<IN>){ print OUT $_; } close(IN); close(OUT); open(OUT,">vote_log.cgi"); print OUT @log_lines; print OUT "\n1;\n"; close(OUT); } &make_vote_ck('set'); } } sub reset_vote{ my @log; foreach my $i(1..$LAST_ID){ next if !@{$VT[$i]}; $VT[$i][18] = $VT[$i][7]; $VT[$i][19] = $VT[$i][8]; $VT[$i][20] = $VT[$i][0]; foreach my $j(0..8){ $VT[$i][$j] = 0; } push(@log,'$VT['.$i.'] = ['.(join(',',@{$VT[$i]}))."];\n"); } open(OUT,">vote_log.cgi"); print OUT @log; print OUT "\n1;\n"; close(OUT); &make_vote_ck('reset'); } sub make_vote_ck{ if($_[0] eq 'set'){ my @t =localtime($times + 86400); $VT_TIME = timelocal(0,0,5,$t[3],$t[4],$t[5]); $VT_RANK = $VT_RUI = $VT_RECO = $VT_RCRUI = $VT_COUNT = $VT_CTRUI = $VRK_CK = 1; } if($_[0] eq 'reset'){ my @m =localtime($times); $m[4] += 1; if($m[4] > 12){ $m[4] = 1; $m[5] += 1; } $VT_RESET = timelocal(0,0,2,1,$m[4],$m[5]); $VT_RANK = $VT_RECO = $VRK_RS = $VRK_CK = 1; } if($_[0] eq 'restore'){ $VT_RANK = $VT_RUI = $VT_RCRUI = $VT_RECO = $VT_COUNT = $VT_CTRUI = $VRK_CK = 1; } open(OUT,">vote_ck.cgi"); print OUT "\$VT_TIME = '".$VT_TIME. "';\n"; print OUT "\$VT_RESET = '".$VT_RESET. "';\n"; print OUT "\$VT_RANK = '".$VT_RANK. "';\n"; print OUT "\$VT_RUI = '".$VT_RUI. "';\n"; print OUT "\$VT_RECO = '".$VT_RECO. "';\n"; print OUT "\$VT_RCRUI = '".$VT_RCRUI. "';\n"; print OUT "\$VT_COUNT = '".$VT_COUNT. "';\n"; print OUT "\$VT_CTRUI = '".$VT_CTRUI. "';\n"; print OUT "\$VRK_CK = '".$VRK_CK. "';\n"; print OUT "\$VRK_RS = '".$VRK_RS. "';\n"; print OUT "\$LAST_ID = '".$LAST_ID. "';\n"; print OUT "\n1;\n"; close(OUT); }

    • ベストアンサー
    • Perl
  • ファイルの入出力が出来ません。

    perlの初心者です。 cgiを作ろうとしています。 標準出力には出力できても、ファイルの入出力が出来ません。 #!/usr/bin/perl open(OUT,">>test"); print OUT "test data"; close (OUT); と実行しても、エラーもでないのですが、ファイルも作られず、書き込みもされません。 しかし、コマンド名'test'としてセーブして、プロンプトから'perl test'とするとファイルに書き込みされるようです。 perlの設定の問題なのでしょうか? よろしくお願いします。

    • ベストアンサー
    • Perl
  • perlで容量の大きいCSVファイルを開く方法

    perlで容量の大きいCSVファイルを開く方法 ファイル容量の大きいcsvファイルから、必要な項目を抜き出して別ファイルにするプログラムを作成したいと思ってます。 csvファイルが少ない場合は動作したのですが、容量が140MBを超えたデータを読み込もうとすると、ブラウザー表示で何も変化いたしません プログラムは以下のようになってます。 ------------------------------------------------- 略 open(IN,"$inport") || &error(" $inport を読み込みopen出来ません"); flock(IN,1); @lines = <IN>; foreach $lines (@lines) { local(@val) = split("\,", $lines); print "$val[0]"; $dat .= "$val[1]\,$val[5]\n"; } open(OUT,">$dcsv"); flock(OUT,2); print OUT "$dat"; close OUT; 略 ------------------------------------------------- 件数も多いので、foreachを$lines (@lines) としないで($start .. $end)として読み込みの件数を制限して対応しようと考えてましたが、うまくいきませんでした。 ご指導いただけますと幸いです。

    • ベストアンサー
    • Perl
  • ファイルの3行目までを出力したい

    Perl初心者です。 test.txtというファイルがあって、その中の1行目から3行目までを 出力したい場合はどうしたらいいでしょうか? open(FILE,"test.txt") || die "Open Error.\n"; @data = <FILE>; close(FILE); foreach (@data) { print $_; } これだと、ファイルの中身が全て出力されてしまいます。

    • ベストアンサー
    • Perl

専門家に質問してみよう