• ベストアンサー

Perlを利用してテキストファイルのデータを読み込み・書き出し

今作成しているのは、WEB上で仕事の情報をメンテナンス出来るシステムです。HTMLとPerlを利用しています。 その際に、データをテキストファイルにタブ区切りで随時書き込んだり、読込んだりする事でその内容をフォーム上に表示させたりしています。動作はしているのですが、問題が出てきました。1行単位で書き出したり読込んだりしているのですが、最近データ数(件数)が増えてきましたので、処理に時間がかかり書き出しの途中で他のページに切り替えると途中までしかデータが書き出されません。いろいろなパソコンでこのシステムを利用するので、スペックの低いマシンでも、ある程度の処理の高速化を図り、なるべくこのような事が起きないようにしたいのですが良い方法はありますでしょうか。通常こういった形でシステムを作成する場合はどのような方法をとるのでしょうか?教えて下さい。掲示板はこのような仕組みで作られていると思いますが、データの数が多い場合はどのようなコードで作成されているのでしょうか? [コード一部] 各データは変数$data[$x]に取り込んでいます。 open(FH,">$filename"); flock(FH,LOCK_EX); for ($x=0;$x<=$count;$x++) { print FH $data[$x],"\n"; } flock(FH,LOCK_UN); close FH;

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

  • ベストアンサー
  • Dpop
  • ベストアンサー率51% (279/544)
回答No.5

#2 です。大変遅くなりました(^^;) サンプルソースを作ってみました。 1レコード/1データである事を前提にしたテストデータを作成し、 それを、バークレーDBへ書き込む例と、バークレーDBから読み出す例です。 動作を確認して頂くために、別々のソースにしましたが、同一ソース内に書き込み, 参照があっても問題ありません。 と言うか、tie~untieの間でバークレーDBに割り当てたハッシュの内容を更新すると、 その内容が、そのままバークレーDBに記録されます。 そのため、単にハッシュ変数を操作するだけで、バークレーDBへの更新が行われる。 と考えて頂ければ良いです。 参照する場合も、バークレーDBから読み出されるので、例の様に初めに一気に読み込んで置く必要は無く、 単にハッシュを参照すれば良い事になります。 この例では、バークレーDBとしてAnyDBM_Fileを利用しています。 AnyDBM_Fileは実際には、システムで利用できるバークレーDBシステムの どれかに割り当ててくれる機能を持つパッケージで、 実際には、SDBMやGDBMに割り当てられます。 どのパッケージが利用できるのか、不明な時にはAnyDBM_Fileを宣言するのが良いでしょう。 この例では、1レコード/1データである事を前提にしていますが、 リファレンスを利用した複雑な型のデータを取り扱う場合、MLDBMを利用する必要があります。 MLDBMを利用する場合には、useの部分を use SDBM_File; use MLDBM qw (SDBM_File); use Fcntl; などの様にして、tie文を tie(%h, 'MLDBM', $db, O_RDWR|O_CREAT, 0666); としてください。 MLDBMでは、利用するDBMを指定する必要があります。(省略すると、SDBMが利用されます。) そのため、事前にどのDBMが利用できるのか、調べて置く必要があります。 ----------^^^^^ ここから ^^^^^---------- #!/use/local/bin/perl # # テストデータを作成する。 # mk_data.pl # created by Dpop # $f = "data.txt"; open(OUT, ">$f"); foreach $a (0 .. 25) { print OUT (chr(65+$a))x 5, "\n"; } close(OUT); exit(0); ----------$$$$$ ここまで $$$$$---------- ----------^^^^^ ここから ^^^^^---------- #!/use/local/bin/perl # # テストデータをバークレーDBへ保存する。 # save_db.pl # created by Dpop # use Fcntl; use AnyDBM_File; $f = "data.txt"; $db = "data.db"; $c = 1; open(IN, $f) or die "open error $f"; tie(%h, AnyDBM_File, $db, O_RDWR|O_CREAT, 0666); while(<IN>) { chop; $h{$c} = $_; $c++; } close(IN); untie(%h); exit(0); ----------$$$$$ ここまで $$$$$---------- ----------^^^^^ ここから ^^^^^---------- #!/use/local/bin/perl # # バークレーDBを読み込んで表示する。 # load_db.pl # created by Dpop # use Fcntl; use AnyDBM_File; $db = "data.db"; tie(%h, AnyDBM_File, $db, O_RDWR, 0666); while(($key, $value) = each(%h)) { printf("key => %-3s data => %s\n", $key, $value); } untie(%h); exit(0); ----------$$$$$ ここまで $$$$$----------

simple-t
質問者

お礼

サンプルコード大変助かります。本当にありがとうございます。確認次第ご報告&ご質問をお願いするかもしれませんので、宜しくお願い致します。

すると、全ての回答が全文表示されます。

その他の回答 (4)

  • hikomin
  • ベストアンサー率63% (40/63)
回答No.4

前回答に補足。サンプルコードですが、うっかりミスが2ヶ所。(ぱっと書いたので粗が目立ちます。) print "処理を受け付けました。" # <- セミコロンが無い 正:print "処理を受け付けました。"; open FH, "> $filename"; # <- 上書きモードで開くとロックされていないにも関わらずファイルが消去される 正: open FH, "+< $filename"; # 読み書きモード flock FH, 2; seek FH, 0, 0; print $_ ."?n" foreach (@data); truncate FH, tell FH; # ファイルサイズを合わせる(ファイルサイズが小さくなった時に、ファイル末尾に前のデータが残ってしまったりするので必要) close FH; あと、"?n"とかになっていますが、バックスラッシュで書いたので化けたようです。"¥n"等に適時読み替えてください。 以上参考まで。

simple-t
質問者

お礼

補足ありがとうございます。 確認出来次第再度質問をお願いするかもしれませんので、もうしばらく宜しくお願い致します。

すると、全ての回答が全文表示されます。
  • hikomin
  • ベストアンサー率63% (40/63)
回答No.3

時間がかかる処理をバックグラウンドで行なう為には、forkで時間のかかる処理を子プロセスとして分離し、Apacheに対する標準出力を閉じ、プロセスが終了したと騙す方法があります。 コード例にならい、@dataにデータが保存してあるとすれば、例えば次のような処理が考えられます。 -- if( my $pid = fork ){ # 親プロセスで実行される # プログラムの応答 print "Content-Type: text/plain;charset=utf-8?n?n"; print "処理を受け付けました。" # Apacheへの標準出力を閉じる close STDOUT; # 子プロセスが終了するのを待つ wait; } else{ # 子プロセスで実行される # Apacheへの標準出力を閉じる close STDOUT; # ファイル書き出し open FH, "> $filename" or die "file open error"; flock FH, 2; # 排他ロック seek FH, 0, 2; # ロック直前にポインタがずれた時に備えて print FH $_ . "?n" foreach (@data); # ここでは単純に全行書き出している close FH; # ハンドルを閉じるとロックも解除 } -- バッファリング設定($|)が0(デフォルト)ならば親プロセス内で予め定めた変数(@data等)は持ち越されます。(if fork以前の部分のみ。) これは、単純に時間のかかる処理を子プロセスにやらせているだけで、処理が速くなるわけではありません。また、処理結果をブラウザ出力に反映する事は出来ません。(処理終了を待たずにブラウザを開放している為。) 複雑になりがちな他、うっかり妙な事を書くと子プロセスが殺されずに残ったり(ゾンビプロセスとか言いますが)するので、十分なエラー判定を行なってください。また、forkは使えないサーバもありますので要注意です。 処理の高速化を考えるのならば、今回のような行ベースのデータであれば、RDBは有効な選択肢の一つです。C/S型だと導入や管理が難しい部分はありますが、SQLiteならば導入もバックアップも容易でオススメです。(大規模なものには不向きですが。) 検索をかけると、多くの情報が得られると思います。(perl fork, DBD::SQLite等)

simple-t
質問者

補足

ご返答ありがとうございます。お返事遅くなりまして申し訳ございません。M(_ _)M 恥かしながら、すぐに教えて頂いた内容が理解出来ずですので、一度コードを自分でも作ってみて確認してみたいと思います。再度質問をさせて頂くかと思いますが、よろしくお願い致します。

すると、全ての回答が全文表示されます。
  • Dpop
  • ベストアンサー率51% (279/544)
回答No.2

Web屋です。OkWebの様なサイトを設計, 開発しています。 同業者かな? こう言う場合は、DBでしょう。 DBと言っても、RDBではなくバークレーDBです。 Perlの標準パッケージに、バークレーDBとやり取りするための機能が用意されており、 tie命令でハッシュ変数を結びつけて(「tie変数」なんて呼びます。)挙げます。 後は、普通にハッシュ変数を利用するだけでDBへ書き込まれたり、 DBから読み出されたりします。 テキストファイルと違ってDBですので、処理は高速だし、 ファイル内容全てを予め読み込んで置く必要はありません。 ハッシュ変数に単純にアクセスすれば良いだけの事です。 現在、配列にデータを取り込んでいると言う事なので、 これを全てハッシュ変数に置き換える必要がありまずか、 それだけの価値はあると思います。 サンプルソースは。。。 今適当なソースが見当たらないので、後ほど適当なソースをアップしましょう。 閉じずに、もう少し待っていてください。

simple-t
質問者

補足

返事が遅くなり申し訳ありません。解答ありがとうございます。実は全くのPerl初心者ですので、教えて頂いた内容がすぐに理解出来ずです(>_<!) しかし、時間がかかるかもしれませんが、バークレーDBに関して調べてみます。それからまた質問を挙げさせて頂きます。ありがとうございまいた。閉じずに置いておきますので、引き続きアドバイスお願いします。

すると、全ての回答が全文表示されます。
noname#14464
noname#14464
回答No.1

 一般的な掲示板では、スレッドごとに別のログファイルを使うとか、一定記事数(60行くらいが多いでしょうか。最大でも100行くらいですね)に古いデータを過去ログとして別の場所に待避するなどでしょう(うちで使っている掲示板は、スレッドごとに別ファイルを使用しています)。  しかし、この場合はあまりログを過去ログとするケースではないのではないでしょうか? 簡単に考えうるのは、一つの記事ごとに別のファイルを使うとか、一定記事数ごとに、記事を保存するファイルを変えるとかではないでしょうか?(記事数の3桁目をファイル名に使用するなど)

simple-t
質問者

補足

ご返答ありがとうございます。 例えばお答えいただいたように一定の件数(例えば100行中60行分)を別のファイルに変える事で書き込みや読み込みの処理速度は1つのファイルにするよりも早くなるのでしょうか。出来れば、1つのフォーム内で全ての件数(100行分)を一覧として表示させたいのですが・・・。分かりにくくてごめんなさい。M(_ _)M

すると、全ての回答が全文表示されます。

関連するQ&A