効率的なデータ統合プログラム作成とは?

このQ&Aのポイント
  • 2つのデータを統合するプログラムをperlで作成しています。
  • 最終データが必要な際に、2つのデータを読み込みながら更新処理を行っています。
  • しかし、データ件数が多くなると非効率になり動作しなくなる問題があります。
回答を見る
  • ベストアンサー

2つのデータを統合を効率よく出来ません

2つのデータを統合を効率よく出来ません 2つのデータを統合するプログラムをperlで作成しています。 データはテキストでサーバー環境にあります。 クラスデータ 1,1-1,1年 2,1-2,1年 3,1-3,1年 4,2-1,2年 5,2-2,2年 6,3-1,3年 7,3-2,3年 名前データ 1,田中,たなか 2,伊藤,いとう 3,斎藤,さいとう 4,上野,うえの 5,大阪,おおさか 6,福島,ふくしま 7,矢部,やべ 最終データ 1,田中,たなか,1-1,1年 2,伊藤,いとう,1-2,1年 3,斎藤,さいとう,1-3,1年 4,上野,うえの,2-1,2年 5,大阪,おおさか,2-2,2年 6,福島,ふくしま,3-1,3年 7,矢部,やべ,3-2,3年 以下のプログラムで動作させていますが、件数が多くなると非常に非効率となり動作しなくなります。 ※公開用にプログラムを修正しています。おかしい部分があるかもしれません。 (ここから) 略・・・ open(DB10,"<$namefile") || next; flock(DB10, 1); @lines10 = <DB10>; close(DB10); foreach $lines10 (@lines10) { ($seq,$name,$kana,$classnew,$nennew) = split("<>", $lines10); open(DATA,"$classfile")|| die &error(" $classfile を読み込みopen出来ません"); flock(DATA,1); @lines11 = <DATA>; close(DATA); foreach $lines11(@lines11){ ($seq1,$class,$nen) = split("<>", $lines11); $classnew = $class; $nennew = $nen; open(DATA1,"$classfile")|| die &error(" $classfile を読み込みopen出来ません"); flock(DATA1,1); @line21 = <DATA1>; close(DATA1); foreach $line21(@line21){ local(@val) = split("<>", $line21); if($seq1 == $val[0]){ $line21 ="";} push(@new,"$line21"); } push(@new,"$seq1<>$class<>$nen\n"); open(DATA1,">$classfile")|| die &error(" $classfile をwrite出来ません "); flock(DATA1,2); print DATA1 @new; close(DATA1); @new = ""; } @new1 = ""; open(DB12,"<$namefile") || &error("Can't open $namefile"); flock(DB12, 1); @line12 = <DB12>; close(DB12); foreach $line12 (@line12) { local(@val1) = split("<>", $line12); if($seq == "$val1[0]"){ $line12 ="";} push(@new1,"$line12"); } push(@new1,"$seq<>$name<>$kana<>$classnew<>$nennew<>\n"); open(DB12,">$namefile")|| die &error(" $namefile をwrite出来ません "); flock(DB12,2); print DB12 @new1; close(DB12); } } (ここまで) プログラムの流れとしては(私が解釈している)、2つのファイルを1件読み込み更新を最終行へ毎回行っているというものになります。 恐らく、もっと効率の良い方法があるとは思っているのですが、アイデアが浮かばす止まってしまいました。 このプログラムではなく別のものでもOKです。私がこの方法しかしらないのでこの記述をしています。 ファイルの更新系でアドバイスがいただけたらと思い書き込みます。 ご指導よろしくお願いいたします。

  • Perl
  • 回答数2
  • ありがとう数22

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

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

最初に確認ですが (1) $namefile: 名前データ→最終データに書き換え、クラスデータの無いものはそのまま $classfile: クラスデータ→名前データにある行は削除 別のファイルに出力するのではないですね? (2) 一つ目の値で統合するのですね?この値は重複してたりしませんね? (2) スクリプトでは'<>'が区切り文字として使われていますが、例題はカンマで区切られています。 どちらが正しいのでしょうか? (2) サーバー上ということですが、実行はそのサーバーにログインして「perl スクリプト」のように実行ですか? それとも、WebブラウザからCGIとして実行ですか? (3) Perlのバージョンは5.?以上ですよね? まず、効率が悪い点は $namefileの1行に対して ・$classfileを最初から最後まで全部読み込むのを2回、該当行を削除した書き出しを1回 ・$namefileに途中まで更新した内容の書き出しを1回 行っていることです。ファイルの読み書きは、他の内部的な処理に比べて非常に時間のかかるものです。極力減らすのが高速化につながります。 今回のでいえば、読み込みと書き出し1回ずつ、それぞれのファイルで計4回で済みます。 後、細かい点で言えば ・flockは必要ですか? 処理中にファイルが書き変わることがあるのなら、読み書きモードでopen→flock、処理が全て終ってからclose、として、この処理の最初から最後まで他の処理が入らないようにするべきです。 そうでないなら、flockの必要はありません。 ・local(@val1) = split("<>", $line12); 一時的なローカル変数としてなら、localではなく、my を使うことをお勧めします。詳しくはネットで検索 但し、perl 4以前なら myは無いのでlocalになります。 ・@new1 = ""; リストの初期化なら、空リスト()の方がよいかと。 open(DATANAME,$namefile) ; @lines11 = <DATANAME>; #$namefileから読み込み close DATANAME ; open(DATACLASS,$classfile) ; @lines12 = <DATACLASS>; #$classfileから読み込み close DATACLASS ; %namehash=() ; # seqをキーにしたハッシュテーブルを作る # namefileからハッシュテーブルを作る foreach $lines11(@lines11){ @val = split(/<>/,$lines11) ; if ( $#val >= 1 ) { #<>の区切りがあったら、ハッシュに登録 $namehash{$val[0]} = $lines11 ; } } # classfileとハッシュテーブルとを照合する foreach $lines12(@lines12){ @val = split(/<>/,$lines12) ; if ( $#val >= 1 ) { #<>の区切りがあったら、ハッシュと照合 $seq = shift @val ; # @valの先頭を取り出す if ( exists $namehash{$seq ) { # 対応するキーがあったら $namehash{$seq} =~ s/\n//; # 行末の改行コードを削除 $namehash{$seq} .= "<>" . join("<>",@val); # $seqを除いた部分を追加する $lines12="" ; # foreachでは、ループ変数を書き換えると、元のリストの内容が書き変わる } } } #結果の書き出し open(DATANAME,">" . $namefile) ; #全てのキーについて、その内容を書き出す foreach $key (sort ( keys %namehash )) { print DATANAME $namehash{$key} ; } close DATANAME ; #残ったクラスデータの書き出し open(DATACLASS,">" . $classfile) ; print DATACLASS @lines12 ; close DATACLASS ;

hihin2003
質問者

お礼

kmeeさん 回答ありがとうございます。 >別のファイルに出力するのではないですね? はいその通りです。 >一つ目の値で統合するのですね?この値は重複してたりしませんね? はい。そうですが、重複する可能性はあるかもしれません。 説明不足で申し訳ございません >スクリプトでは'<>'が区切り文字として使われていますが、例題はカンマで区切られています。 >どちらが正しいのでしょうか? ごめんなさい。「<>」になります。 >サーバー上ということですが、実行はそのサーバーにログインして「perl スクリプト」のように実行ですか? >それとも、WebブラウザからCGIとして実行ですか? WebブラウザからCGIとして実行になります。 >Perlのバージョンは5.?以上ですよね? はい。5以上になります。 >但し、perl 4以前なら myは無いのでlocalになります。 ありがとうございます。そうなんですね。勉強になります。 >@new1 = ""; >リストの初期化なら、空リスト()の方がよいかと。 ありがとうございます。 今後はそのようにいたします。 ハッシュについても、まだ完全に理解し切れていないですが、今後今回の件で使う機会がありそうなので これを機会にちょっと使ってみようかと思います。

その他の回答 (1)

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.1

実際の件数が不明なのでうまくいくかはわかりませんが、1つの配列にクラスデータと名前データを 読み込んで処理するのが効率的だと思います。 use strict; my $list = join '', map { /(.*年)\n$/ ? $1 : $_ } sort { $a <=> $b } <DATA>; print "$2$1\n" while $list =~ /\d+(.*?年)(.*)\n/g; __DATA__ 1,1-1,1年 2,1-2,1年 3,1-3,1年 4,2-1,2年 5,2-2,2年 6,3-1,3年 7,3-2,3年 1,田中,たなか 2,伊藤,いとう 3,斎藤,さいとう 4,上野,うえの 5,大阪,おおさか 6,福島,ふくしま 7,矢部,やべ ここでは <DATA> ファイルハンドルを使っていますが、2つのファイルから読み込むには 次のようにします。なお、ファイルのオープン・クローズのコードは省略しています。 my @list = <FILE1>; # クラスデータ push @list, <FILE2>; # 名前データ my $list = join '', map { /(.*年)\n$/ ? $1 : $_ } sort { $a <=> $b } @list; print "$2$1\n" while $list =~ /\d+(.*?年)(.*)\n/g;

hihin2003
質問者

お礼

kumozさん 回答ありがとうございます。 なるほど1つの配列にまとめて対処するんですね。 ちょっとやってみます!!

関連するQ&A

  • perlでファイルのデータの一部を削除したい

    久しぶりにPerlを操作してファイルのデータの一部を削除したいのですが、削除出来ずに困っております。 3年以上触っていないため、過去のファイル等々を見ながらやってましたが、結果は出来ずじまいで停滞して困っております。 ご指導いただけないかと思い、書き込みしました。 ファイル名 $tmpfile データ構成  no,名称 1<>フレーム 2<>レンズ 3<>カメラ 4<>ファインダー 5<>めがね 6<>ズーム これで「めがね」を削除したいのです。 データnoで削除したいと思っておりますが、うまく動作しません。 以下ソース open(DB,"$tmpfile") || &error("Open Error : $tmpfile"); flock(DB, 1); @lines = <DB>; @new=(); foreach $line (@lines) { $flag=0; ($no,$meisyo) = split("<>", $line); foreach $x (@DEL) { if ("$x" eq $tmpnum) { $flag=1; last; } } if ($flag == 0) { push(@new,$line); } } open(OUT,">$tmpfile") || &error("Write Error : $tmpfile"); print OUT @new; close(OUT); ここまで 上のソースですと、エラーこそならずに(そうみえているだけかもしれません)終わりますが、データ削除が正しく出来ませんでした。 説明不足があるかもしれませんので、指摘いただけますと幸いです。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • データ件数が多くなってもエラーにならない方法

    perlで、URLからサイトが表示されるかを確認するプログラムを作成しました。 ただ現在の方法だと、チェックするデータ件数が多くなるとエラーとなってしまいます。 現在のソース (ここから) open(DB,"$memfile") || &error("Open Error : $memfile"); flock DB, 2; @lines = <DB>; close(DB); @new=(); foreach $line (@lines) { $flag=0; ($num,$url,$title) = split("<>", $line); &url_ckeck("$url"); $data = "$num<>$url<>$title\n"; $data =~ s/\n<>/<>/g; $data =~ s/\r\n<>/<>/g; $data =~ s/\r<>/<>/g; push(@new,$data); } open OUT, "> $memfile"; print OUT @new; close OUT; (ここまで) この方法だと300件を超えると「ページが表示出来ない」エラーがでてしまうので、 チェックの方法を小分けしてエラー回避できないかを考えているのですが、とのようにすれば良いのかがわからず先に進められなくなってしまいました。 プログラムも見よう見真似で作成しているのでおかしな箇所が多々あるかと思いますが、助けていただけないかと思い書き込みました。 「$line (@lines)」を「$start .. $end」として件数を小分けにしてボタンを表示させてクリックして進められるようにしてみたらとも思いましたが データがうまく更新出来ずにいます。 不明点はご指摘ください。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • CSVデータの編集の際の重複チェックの方法

    今、data.csv(カンマ区切り)として、左から順位、名前、性別という3項目で、10人程度のリストデータがあります。 data.csv(カンマ区切り)を編集するようにしていますが、順位は重複してはならないので、重複していたらエラーを出したいのですが。。。 ($rank,$name,$sex) = split(/\,/,$line); です。 open(IN,"$logfile") || &error("ファイルが開けません"); @lines = <IN>; close(IN); # 情報の書換え foreach $line (@lines) { ($rank,$name,$sex) = split(/\,/,$line); $line = "$in{'rank'},$name,$sex\n";} push(@new,$line); } # ファイルを更新 open(OUT,">$logfile") || &error("ファイルが開けません"); print OUT @new; close(OUT);

  • データ削除方法について

    現在以下のプログラムでデータを削除しています。 (ここから) print "Content-type: text/html\n\n"; open(IN,"<$memfile") || &error("Can't write $memfile"); flock(IN, 2); @lines = <IN>; foreach $lines (@lines) { ($seq,$name) = split("<>", $lines); if("$del" eq "$seq"){ next; }else{ $data .= $lines; } } close IN; open OUT, "> $memfile"; flock OUT, 2; print OUT $data; close OUT; print "<html>完了しました<br><a href=?mode=menu>戻る</a></body></html>"; exit; (ここまで) このプログラムでも削除出来るのですが、foreachの処理で対応するとパフォーマンスが悪いのかな? と思ってます。(ハッキリとはわかりません) 件数が多くなるとサーバーエラーになるのかな?と思ってます。 もっと効率の良い方法がありましたらと思い質問いたしました。 perlを独学で学んでいるので、見る人が見ると変なつくりだと思ってます。 以上、よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • アクセスカウンターでデータが削除されてしまう。

    プログラムのアクセスカウンターをするプログラムを作成しましたが、アクセスカウンターのデータが消去されてしまいます。 プログラム抜粋 (ここから) #アクセスカウンターカウント数計上ロジックを入れる $data = ""; open(IN11,"$countcsv") || &error(" countcsv をopen出来ません"); flock(IN11,1); while($lines11 = <IN11>) { ($seq,$acc) = split("\,", $lines11); if ($page == $seq) {$acc = $acc +1; } $data .= "$seq,$acc\n" } close(IN11); open(IN21,">$countcsv")|| &error(" countcsv をopen出来ません"); flock(IN21,2); print IN21 "$data"; close(IN21); (ここまで) count.csvの構成は以下のようになってます。 (ここから) 1,0 2,0 3,0 4,0 5,0 6,0 7,0 8,0 9,0 10,0 (ここまで) カウンターを計測するデータ(行数)が多いと、データが全てなくなってしまいます。 ファイルロックの方法に問題があると思ってますが、どこが悪いのかがわかりません。 ご教授いただけますと助かります。 よろしくお願いいたします。

    • ベストアンサー
    • 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
  • 受信データの分割

    @atai = split(/&/,$buffer); foreach $atais(@atai){ ($key, $value) = split(/=/, $atais); $FORM{$key}=$value; } 多分上記のようなことをすればできるのだと思うのですが、 自分の力でコードをかきたくて、1行ずつかいてみました。 #ファイルオープン open FILE,"<log.log"; flock(FILE,2); @log_data=<FILE>; flock(FILE,8); close FILE; foreach(@log_data){ (@log) = split(/=/,$_); } foreach(@log){ ($log_data) = split(/&/,$_); } foreach($log_data){ ($key,$value) = split(/=/,$_); } $FORM{$key}=$value; このようにかいてみたのですがうまくいきません。 よろしくお願いします。

    • ベストアンサー
    • CGI
  • ファイルの配列のリストデータ(変数)の特定の変数の値だけを変えたい。

    サブルーチンの下のコードで表題の事をやろうとしていますが、 1)値が変わりません。($sinkou) 2)「エラー2','メッセージファイルが開けませんでした.」と表示されてしまいます。 3)モジュールで実現する方法がアったら教えてください。 よろしくお願いします。 sub SET_sinkou{ my($number,$name,$sinkou,@new,$line,$hanabi,$sinkou2); $lockfile = "$tmp_dir$cmd{'log'}\.lock"; &lock; if (!open(DB,$message_file)) { &Error('エラー1','メッセージファイルが開けませんでした.'); } while(<DB>){ ($number,$name,$sinkou) = split(/\t/,$_,3); if($number == $_[0] ) { $sinkou = $_[1]; $sinkou2 = $_[1]; #チェック用 $hanabi='花火'; #チェック用 $line=join(' ',$number,$name,$sinkou); push @new,$line; } else{ $line=join(' ',$number,$name,$sinkou); push @new,$line; } } close DB; if (!open(DB,">$message_file")) { &Error('エラー2','メッセージファイルが開けませんでした.'); } foreach(@new){ print DB $_; } close(DB); unlink($lockfile); &Html_head; print <<"EOF"; $body <h2><strong>変更しました。$sinkou=$sinkou=2=$_[0]=$_[1]=$hanabi</strong></h2><p> <!--$sinkou=$sinkou=2=$_[0]=$_[1]=$hanabiは値のチェック用--> </body></html> EOF }

    • ベストアンサー
    • Perl
  • データファイルのソート方法について

    perlで使用している、データファイルのソート方法について質問いたします。 やりたいこと。 1.ファイルを読み込み 2.指定されている文字列でソートをして 3.同じファイルに格納する 以上になります。 以下のファイルにデーターが入ってます・ namedata.cgi 田中,4 佐藤,2 鈴木,1 水野,3 このファイルをソートして以下のように並び替えて保存したいと思ってます。 スクリプトを実行後 namedata.cgi 鈴木,1 佐藤,2 水野,3 田中,4 となっていてほしいのです。 スクリプト sort.cgi 略・・・ #ふぁいる読み込み open(DB,"<$file") || &error("Can't write $file"); flock(DB, 1); @lines = <DB>; close(DB); #ソート @result = sort { $a <=> $b } @lines; #ファイル書き込み open(DB,">$file") || &error("Can't write $file"); flock(DB, 2); print DB "@result"; close(DB); ファイルの2項目目が分からないからソートされていないような気がするのですが、記述方法がわかりません。 ファイル読み込み時にforeachを使用して読み込まないとだけなのでしょうか? うまく説明できていないかもしれませんが、よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • データの抽出、配列操作で教えて下さい。

    こんばんは、お世話になります。 配列操作で教えて頂きたく。 次のデータをuser.datとします。 1<>yamada<>99999<> 2<>tanaka<>22222<> 3<>suzuki<>10101<> 4<>yamada<>12345<> 5<>yamada<>55555<> user.datの中からyamadaの行だけ取り出して 3番目の数字データでソート表示したいのですが なかなかうまくいきません。 open(F,"user.dat"); @load_txt = <F>; close(F); foreach $data (@load_txt){ if((split(/<>/,$data)[2]) eq "$usid"){push(@txt_lines,$data);} } @txt_lines = sort{(split(/<>/,$a))[2] <=> (split(/<>/,$b))[2]}@txt_lines; 上記でおかしな所ありましたらご教示頂きたく 宜しくお願いします。

    • ベストアンサー
    • Perl

専門家に質問してみよう