• ベストアンサー

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;

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

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

  • ベストアンサー
  • 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

  • ファイルの読み書きについて教えてください。

    フォームから投稿されてきた内容を、テキストファイルに書き込むphpを作っているのですが、 flock関数で、誰かが書き込んでいるときに他の人が書き込めないようにファイルをロックしたいと思っています。 以下のソースでLOCK_EXとLOCK_UNは正しく動作するでしょうか。 いまいち不安で困っています。 //ポストされてきた内容が$nameに入ります $name = $_POST['name']; $fh=fopen("test.txt","a+"); flock($fh, LOCK_EX); fwrite($fh,"$name \n"); flock($fh, LOCK_UN); fclose($fh); どなたか詳しい方教えてください。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • phpのファイル操作について

    ファイル操作について、上手く作動しなくて困っています。 PHPは今勉強している最中で、ほとんど初心者です。 ページを開いたときに、テキストファイルに書き込む操作をしたいのですが、うまくいきません。 ローカルホストで試したときには、きちんと書き込むことができるのですが、 借りているサーバーで試すと、”ファイル書き込み失敗”と出てしまいます。 http://ribbon.to/ こちらのサーバーを借りています。 原因がわからないので、少しでもいいのでお力をいただきたいです。 よろしくお願いします。 ここからPHPです $folder = "hoge"; if (!file_exists($folder)) { mkdir($folder,0733); } $filename = "$folder/".date("m").".txt"; $fh = @fopen($filename, "a"); if ($fh == FALSE) { exit("ファイル書き込み失敗"); }else { // 排他ロック flock($fh, LOCK_EX); // メッセージ作成 $msg = date("Y/m/d H:i:s"); // 書き出し fputs($fh, $msg); // ロックを解除 flock($fh, LOCK_UN); // ファイルを閉じる fclose($fh); }

    • 締切済み
    • PHP
  • ファイル作成

    $fp = fopen($filename1,"a+"); flock($fp,LOCK_EX); fputs($fp,$kisai); flock($fp,LOCK_UN); fclose($fp); としてファイルがなかったら作成するようにして$kisaiの内容を保存するようにしました。 それで問題なく実行できます。 しかし文字コードがANSIで保存されてしまいます。 UTF-8で保存させるようにするにはどうしたらよいでしょうか

    • ベストアンサー
    • PHP
  • ファイルにデータを書き込むときにもとのデータを消去

    データを1個のファイルに保存しているのですが、データを更新するときに現在書き込まれている中身を一度消去してから書き込みたいのですが、どのような方法がいいのでしょうか。 $fo=@fopen($book,"r+") or die(); flock($fo, LOCK_EX); ~読み込んで書き込み用のデータ処理~ fseek($fo, 0); fwrite($fo, $buff); flock($fo, LOCK_UN); fclose($fo); と、していますが、先頭に追加されるだけで、これまでのデータが残ったままです。 一度ファイルをまっさらにするか、一度ファイルを削除して新規に作成しようかとも思いましたが、アクセス数が多くなる予定なので、あまり良い方法ではないと考えています。 このような場合、一般的にどうすればいいかご教授願えればと思います。 よろしくお願いいたします。

    • ベストアンサー
    • PHP
  • CSVデータの項目名からファイル名を付ける

    CSVデータからHTML拡張子で書きだすCGIを作成してます。 参考になるサブルーチンをネットで見つけたのですが、行の上から順に001.html・・・・のファイル名になり、CSVデータの項目にあるデータでファイル名を付けたいと思いますが、どのように処理したらよいかわかりません。アドバイス願います。 -------------------------------------------------------------- サンプルのCSVデータ(2行目のファイル名をファイル名につけたいです。) タイトル,ファイル名,会社名,担当者,URL 山ちゃん,yamadaya,山田屋,山田,http://www.oooxxx.oo 最上ちゃん,mogami,最上商店,最上,http://www.oooxxx.oo 南ちゃん,minami,南喫茶,南,http://www.oooxxx.oo -------------------------------------------------------------- コード: $list = join('',@csv); @list = split(/\n/, $list); for($count=1;$count<@list;$count++){ $html = $template; $filename = sprintf("%03d.html",$count); @listinfo = split(/\,/,$list[$count]); for($cnt=0;$cnt<@listinfo;$cnt++){ $no = sprintf("<!--%02d-->",$cnt); $html =~ s/$no/$listinfo[${cnt}]/g; } flock(FH, LOCK_EX); open(FH,">$filename"); print FH $html; close(FH); flock(FH, LOCK_NB); }

  • 【PHP】csvファイルへの書き出し(1行追加or1行上書き)について

    【PHP】csvファイルへの書き出し(1行追加or1行上書き)について はじめて利用させていただきます。 現在、PHPにて書き出し処理を作成している者ですが、csvファイルへの書き出しの際に先頭行にスペースが大量に入ってしまい、困っています。 自分なりに調べてみたのですが、原因がわかりません。詳しい方からのアドバイスをお願いしたいです。 【行っている処理】 1.csvファイルを1行ずつ読み込み、配列に格納 2.既存のデータに同じ名前の人が存在する場合は、その行の配列の内容を変更(存在しない場合は配列の末尾に1件追加) 4.現在ファイルにあるデータを削除(丸めこみ)し、配列データを書き書き出し 【ソース】(書き出し処理に問題があると思ったため、それ以外の部分は割愛させていただきます) <?php $filename = 'data.csv'; $file = fopen($filename, "r+" ); $name = array(); if(flock($file,LOCK_SH)){ while( $data = fgetcsv( $file, 1000, "," ) ) {   //配列に格納 } //配列の内容変更or新規に一行追加処理 //現在のファイル内のデータを削除(まるめこみ) ftruncate($file, 0); //1行分のデータをカンマ区切りで結合し、書き出し for($i = 0; $i < $count; $i++){ $ins = $name[$i]; $ins .= ','; $ins .= $number[$i]; //書き込み失敗時のエラー if(fwrite($file, "$ins\n" ) === FALSE){ print("ファイル書き込みに失敗しました"); }else{ //処理なし } } //ファイルロック解除 flock($file, LOCK_UN); }else{ print("ファイルロックに失敗しました"); } 【csvファイルの中身(処理実行前)】 佐藤,1234 田中,12345 中村,9876 【csvファイルの中身(処理実行後)】                       佐藤,1234 田中,12345 中村,9876 以上です。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • 新しいログの書き込み

    新しいログを追加で上に書き込む方法はどのようにしたらよいのかアイディアがありません。 ご教授願います。 if($mode eq "html"){ flock(FH, LOCK_EX); open(FH,"${datafile}"); @html = <FH>; close(FH); flock(FH, LOCK_NB); $html = join('',@html); @html = split(/$chtml/ , $html); $html[1] = $data; $html = join($chtml,@html); flock(FH, LOCK_EX); open(FH,">$datafile"); print FH $html; close(FH); flock(FH, LOCK_NB);

  • xmlファイル(UTF-8)への書き出し時の改行

    こんばんわ。 息詰ってしまったので質問をさせていただきます。 無理矢理な書き方をしているのがいけないのか PHPからXMLファイルへ書き出しをすると改行として認識されず文字として認識されてしまいます。 どのたかご存じの方がいましたらアドバイスをお願いします! ■index.php(UTF-8、LF) //書き出しに関してのスクリプトです。 $xmlfile="xml/samplexml.xml(xmlファイルまでのパスとxmlファイル名)"; //xmlに書き出しデータ作成 $xmldata = "<xmldata><データ>データだよ</データ><果物>いちご</果物><野菜>キュウリ</野菜></xmldata>"; $fp = @fopen($xmlfile , "a") or err_msg("XMLが開けません。"); flock($fp , LOCK_EX); fputs($fp , $xmldata . "\n"); flock($fp , LOCK_UN); fclose($fp); ■samplexml.xml(UTF-8、LF) <?xml version="1.0" encoding="UTF-8"?> <xmldata><データ>データだよ0</データ><果物>ばなな</果物><野菜>キャベツ</野菜></xmldata> ※ここに改行をさせてデータを追加したい。。 上のスクリプトの結果は改行されずに「\n」も文字として改行されずに追加されてしまう。(「\n\r」「\r」でもテストをしてみたのですが結果は同じでした。)

    • ベストアンサー
    • PHP
  • PHPでpopenでファイル出力

    下記のようにプログラムを作成しておりますが、 検索結果全体を読み込むことができません。 全て8000バイトくらいしか読み込むことができません。 何故でしょうか?? | more という文をgrep に追加してもできませんでした。 $fp_r = popen('nkf -e -Lu '.$fileName.' | grep \''ああ'\'',"r"); //ファイルの内容を全て読み込む $body=fread($fp_r,filesize($fileName)); $fp = fopen($preFile, "w"); chmod($preFile,0777); flock($fp, LOCK_EX); fputs ($fp, $body); flock($fp, LOCK_UN); fclose($fp); pclose($fp_r);

    • ベストアンサー
    • PHP
  • ファイルの書き込みについて

    ファイルの書き込みに関する質問です。 下のコードでは、 3行目「$ptr=fopen("kekka.txt", "w"); //ファイルを開く」 に問題があるというエラーが出ます。 しかし、文法的には問題はないと思うのですが・・・。 サーバーには、"kekka.txt"というファイルが置いてあります。 何かアドバイスがありましたら、よろしくお願いいたします。 <?php $x = "こんにちは";  $ptr=fopen("kekka.txt", "w");   //ファイルを開く    flock($ptr, LOCK_EX);      //ファイルをロックする      fputs($ptr, $x);      //書込む    flock($ptr, LOCK_UN);       //ロックを解除する  fclose($ptr);            //ファイルを閉じる ?>

    • ベストアンサー
    • PHP

専門家に質問してみよう