• ベストアンサー

たくさんのファイルを読み込む処理方法 アドバイスお願いします

php5で数百個のログファイル(情報がカンマで区切られているファイル)に記述されている特定の列の数字を足していきたいのですが、このような場合、どのようにカウントするのが良いかよろしければ皆さんからのアドバイスをお願いします。 ここで、ログファイルは日付による連番であり、たまに過去のファイルも修正される可能性があります。ログファイルの中身はそれぞれ最大10行程度で、それぞれの行の特定の列にある数字を扱う。 例:2つのログファイルがあるとして、それぞれ2行2列ある。 20061126.log 1,1 2,1 20061127.log 1,1 2,0 2つのファイルを読み込み、それぞれのhoge[1][1]を足す。 hoge[1][1]の合計結果は「1」みたいな感じにカウントしたいです・・・。これが数百ファイルになると、何か気をつけなければならない点があるかどうか気がかりです。 ログファイルが作成されたら随時カウントしていく専用の別ファイルを作ろうと思ったのですが、過去のログファイルも更新される可能性があるとなると複雑になってしまうのではないかと思い、他の方法を模索しています。 もしよろしければ具体的なプログラムソースなんかを記述していただければこの上なく嬉しいです。 お手数ですが、せめて、使用すべき関数や気をつける点などがありましたらぜひ教えてください・・。 よろしくお願いいたします!

  • JIITE
  • お礼率48% (13/27)
  • PHP
  • 回答数3
  • ありがとう数2

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

  • ベストアンサー
  • gfct9s
  • ベストアンサー率90% (10/11)
回答No.2

カウント用別ファイルを利用する方式で、ログファイルが更新された時に カウント用ファイルを更新する方式で考えてみました。 確かに複雑になってスマートとはいえず、さらにこの内容でうまく伝わるか という問題もありますが、ご参考になるかもしれませんので回答させていただきます。 ■カウント用ファイルのフォーマット (ファイル名1),(ファイル1の更新時間),(…ファイル名1のデータを横に並べたもの…) (ファイル名2),(ファイル2の更新時間),(…ファイル名2のデータを横に並べたもの…)  : というフォーマットで作成します。 ファイル更新時間の取得は、filemtime()関数を用います。 http://au3.php.net/filemtime 「データを横に並べたもの」は、hoge[X][Y]のデータを下記の式で 横0~nの1次元の並びに変換します。 n = X + Y * Xmax (0 <= X,Y) 質問文の例でいくと、カウント用ファイルの内容は下記になります。 20061026,1164466800,1,1,2,1 20061027,1164553200,1,1,2,0 ■カウント用ファイルの更新 定期的にログファイル全体をチェックし、ログファイルが更新されていないか 調べる処理を行って、カウント用ファイルを更新するとします。 チェック処理の実装は下記のとおりです。 (スペースが全角なので、コピー時はご注意ください) $lastcheck = (どこかに保存した前回チェック時刻。初回時0); $dirpath = '…ログファイルのあるパス…'; $d = dir($dirpath); while ($entry = $d->read()) {   if ($entry == '.' || $entry == '..') continue;   $path = $dirpath . DIRECTORY_SEPARATOR . $entry;   if (is_file($path) && $lastcheck < filemtime($path)) {     // …カウント用ファイルを開いて、     // 対応するログファイルの行を更新、無ければ追加…   } } $d->close(); ■集計値の計算 カウント用ファイルを開き、先頭行から合計していきます。 ■考えられる問題と対策 個別のログファイルが巨大なとき、カウント用ファイルも大きくなって 効率が悪そうです。この場合は、カウント用ファイルを月別に分けるなど して対応します。 カウント用ファイルの更新時に、対象ログファイルの行が見つからずに 追加処理を行う、という処理が繰り返されるとパフォーマンスが悪化します。 この場合は、カウント用ファイル内の行の並びをログファイルの日付順に します。例えば20061101.logは1行目、20061126.logの内容は26行目に記録します。 こうすると、先頭行からログファイル名を照合する必要がありません。

JIITE
質問者

補足

これは良いですね!! まず更新されていないファイルはその中身を見ずに済む点、それとカウントファイルが必要な時だけチェックすればよい点が優れています (自分は、カウントファイルは統計専用のページのみ必要なのに、ログファイルと同時にカウントファイルも更新しようとしていました。うまく伝わるか不安ですが、それだとログファイルが2度更新されたらカウントファイルも2度更新され、2度更新された後に統計ページを見るのであれば1度目のカウントファイル更新は意味の無いものになるので効率が悪い) ちなみに、カウントファイルに必要な情報に絞ればそれぞれのログファイルは150バイト程度ですし、毎日ではなく年に多くても200ファイルと考えれば・・・容量に関してはそこまで心配する必要は無くなりそうです。 一つお聞きしたいのは、 >カウント用ファイル内の行の並びをログファイルの日付順に >します。例えば20061101.logは1行目、20061126.logの内容は26行目に記録します。 >こうすると、先頭行からログファイル名を照合する必要がありません。 カウントファイルの指定した行を書き換えるってのは・・・具体的にどうすれば良いんでしょうか、fgetsでファイルポインタを動かしていく感じで良いでしょうか?もしよろしければ回答お願いします とりあえずどうにかなりそうな気がしてきました。わざわざソースコードまで添えた回答ありがとうございます<(_ _)> 助かってとても感謝しています。

その他の回答 (2)

  • gfct9s
  • ベストアンサー率90% (10/11)
回答No.3

《ANo.2の続きです》 気に入っていただけたようで、安心しました。 カウント用ファイルの更新手法は、ANo.2を書いた時点で3つほど 思いついたんですが、おっしゃるようにfgets()でたどりながらその行だけ 更新するというのは、ファイルサイズが可変長のときは、できそうにないですね。 また何か間違うかもしれませんが、思いつきの2つの方法を紹介します。 ■可変長サイズのカウント用ファイルを用いる場合 その月のカウント用ファイルを初めて作成するときは、まず31行分の 空行だけで作成します。 $statsfile = '…カウント用ファイルのパス…'; if (! file_exists($statsfile)) {   $fp = fopen($statsfile, 'w');   for ($i = 0 ; $i < 31 ; $i++) {     fwrite($fp, "\n");   }   fclose($fp); $fp = null; } 例えば20061126.logの行を更新するときは、file()関数 http://au.php.net/file を用いて一旦全行を配列に読み込み、日付に対応する行を更新します。 $logfilename = '20061126.log'; // ファイル名の日付部分を取得 $day = preg_replace('/^[0-9]{6}([0-9]{2})\\.log$/i', '\\1', $logfilename); // カウント用ファイルを読み込んで、対象行を更新 // (ファイル更新日時を保存していますが、これはたぶん不要です) $lines = file($statsfile); $lines[$day - 1] = $logfilename . ",1164466800,1,1,2,1\n"; // ファイル更新 $fp = fopen($statsfile, 'w'); fwrite($fp, join('', $lines)); fclose($fp); $fp = null; ■固定長サイズのカウント用ファイルを用いる場合 初回のカウント用ファイルを空行31行で作るのは同じですが、 改行を含めて1行が(例えば)256バイトになるように、空白で埋めて作成します。 if (! file_exists($statsfile)) {   $fp = fopen($statsfile, 'w');   for ($i = 0 ; $i < 31 ; $i++) {     fwrite($fp, str_repeat(' ', 255) . "\n"); // ここだけ異なる   }   fclose($fp); $fp = null; } 例えば20061126.logの行を更新するときは、先ほど同様 26行を更新します。日付から書き込み位置を計算して、fseek()関数 http://au.php.net/fseek でファイルポインタを移動します。 $logfilename = '20061126.log'; // ファイル名の日付部分を取得 $day = preg_replace('/^[0-9]{6}([0-9]{2})\\.log$/i', '\\1', $logfilename); // 追加(更新)する行の内容 $newline = $logfilename . ",1164466800,1,1,2,1"; $fp = fopen($statsfile, 'r+'); rewind($fp); fseek($fp, ($day-1)*256); fwrite($fp, $newline . str_repeat(' ', 255 - strlen($newline)) . "\n"); fclose($fp); $fp = null; 1行が256バイトになるように空白埋めをするのは、 初回ファイル作成時と同様です。 もし同一ファイルに複数同時アクセスがありそうでしたら、ANo.1の方がおっしゃるように、 flock()を忘れないようにしてください。 ロックが取得できた場合でも、ほかのプロセスがそのファイルに追記している場合もあり、 ファイルポインタが妙なところを見ている場合があるようです。 この場合は、直前のrewind()が対策になるようです。 http://au.php.net/rewind

JIITE
質問者

お礼

なるほど・・・ $lines = file($statsfile); $lines[$day - 1] で対象行を取得するのですね・・・。とても現実的です。 可変長のカウントファイルから情報を読み取るのは固定長より複雑でしょうが、コチラでがんばってみます。固定長の方法も大変参考になります。 カウントファイルの更新さえできてしまえば、後は自分の力で大丈夫です。 ご助力本当にありがとうございました!

回答No.1

気をつけたほうが良さそうだな、と思った点を以下に書きます。 (性能) 1つのファイルの読込みには、ミリ秒単位の時間がかかると思った方がよいです。数百ファイルだと、結構な時間が掛かります。バッチではなく、オンラインのアプリケーション(ブラウザからのリクエストで起動して、ブラウザに結果を表示するアプリケーション)でやろうとしているのであれば、事前にテストプログラムを書いて、時間測定した方がよいかもしれません(私ならそうします)。 (競合) 書込み同士の競合や、読込みと書込みが競合した場合、ファイルが壊れたり、読み取るデータがおかしくなったりすることがあります。書込み、読込みとも、ファイルオープン時にロックをかけた方が安全です。(ファイルロックにはflock関数を使います)。また、余り心配ないと思いますが、デッドロックを防ぐために、個々のファイルの使用が終わった時点で都度ロック解除(とclose)した方がよいです。 (メモリ) でかいファイルの場合、プログラムがまずいとメモリが不足する可能性があります。このケースでは個々のファイルが小さいようなので、余り心配ないと思いますが。 >もしよろしければ具体的なプログラムソースなんかを記述していただければこの上なく嬉しいです。 プログラムを書ける方だとお見受けしましたので、ソースは省略させてもらいます^^ #そもそもの話として、ファイルではなくデータベースに情報を格納した方がよいのでは、と思います。

JIITE
質問者

お礼

回答ありがとうございました<(_ _)>

JIITE
質問者

補足

やはり気をつけるのはそのくらいでしょうか それとデータベースは利用できないです あとは効率の良いカウント方法を募集します。皆さんならどう処理をするか参考にさせてください。 このくらいのファイル数を扱うのは初めてですので・・経験のある方がいましたらお手数とは思いますがぜひ伝授お願いします。

関連するQ&A

  • テキストファイルを1行ずつ別のファイルに分割する

    400行ぐらいのテキストファイルを、1行ずつ別のファイルに分割したいと思います。 ファイル名は**001.txtのように、(**は任意の文字列、数字は連番)なってくれれば嬉しいです。 どなたかこのような処理のできる簡単な方法をご存じないでしょうか? よろしくお願いします。

  • エクセルでの連番の処理

    お世話になります。 1行目に入力されたデータをルールに沿って2行目以下にどんどん追加していきたいと思っています。 まずD1、E1のセルに数字を入れます。 例:D1に3、E1に25 を入れてマクロを実行するとD列の2行目以下に3,4,5,6…25と連番で入力出来るようにしたいのです。 この例ではD列は24行目まで入力されています。そこで2行目から24行目までのA,B,C列にはそれぞれ1行目のA,B,C列と同じデータを入力します。なおA,B,C列は数字、文字列どちらもあります。空白の場合もあります。 この状態でA~E列の1行目のデータを変更し、仮にD1を2、E1を15としたとします。ここで再度マクロを実行すればD25に2、そして順に連番が入りD38に15が入るようにします。 同時に25行目から38行目までのA,B,C列にはそれぞれ1行目のA,B,C列と同じデータを入力します。 これの繰り返しです。 つまり2行目以下のD列で空白の行以下にどんどん連番を入れていく具合です。 前提としてD1、E1は整数しか入りません。またE1の数字はD1より大きいです。ただD1,E1に同じ数字が入った場合、その数字の1行分だけが入力されるようにします。 以上の処理が自動化できるマクロはできますか? アドバイス願います。

  • csvファイルの整理方法を教えてくださいませ

    メモ帳に六桁くらいの数字のかたまりで、,で区切られていて、5列くらいあります。行としては無限に続いているのですが、最初と2番目の列を別のデータに張り替えたいのです。カンマが邪魔して、うまく張ることができません、方法はありますでしょうか? 例 A列        B列     C列 1行目500000E+0, .000000E+0000, .000000E+0000, 2行目.000000E+0, .773600E+0003, .670100E+0000,

  • Location後の処理について

    header("Location:./hoge.php"); などとして画面遷移を行った場合、これより下の行に書かれた処理は行われているのでしょうか? たとえば if (認証処理) { ログファイルに書込 header("Location:./hoge.php"); } とすればログを書いた後に遷移するのはわかりますが、 if (認証処理) { header("Location:./hoge.php"); ログファイルに書込 } 何らかの処理2 とした場合、ログに書き込みや処理2などは実行されているのでしょうか?

    • ベストアンサー
    • PHP
  • Excel VBA等での処理方法

    下記のような2行の表を、1行に組みなおす場合にどのようなこーどが書けばよいか教えてください。お願いいたします。 下記のように表1から表2のように組みなおしたいのですが。A列には必ずしも数字が入っているとは限りません。また、行数も特定されていません。

  • ファイルを読み込んで特定の箇所のデータを表示

    お世話になります。 掲示板のログファイルから、特定の箇所のデータを取得して表示させたいと思っています。 log.cgi(パーミッション666) ログデータの中身 3<><><> 3<><><><><><><> 2<><><><><><><> 1<><><><><><><> 上記のような感じになっており、「<>」と「<>」の間にいろいろなデータが入っています。 1行目の最初の数字が、これまで投稿された記事総数になっており、この数字を取得したいと思っています。 log.cgiを読み込んで、この記事総数を「$allnumber」と記述すれば、その箇所に表示させるにはどのようにすれば良いでしょうか。 ご教授をお願い致します。

    • ベストアンサー
    • Perl
  • 複数のファイル(html・txt)から文字抽出

    こんにちは。 複数のファイルから特定の文字を抽出する方法を考えています。 出来るのか出来ないのかも自分ではわかりませんでしたので質問させていただきました。 やりたいこととしては 50000を超えるファイル内部の特定の記述部分を抜き出したい。 (抜き出す or 残す(その記述以外の文字は不必要なため)) ファイルの中に記述されている残したい文字は user="*****" の*****部分。 *****は数字が連番で振られている。 複数のファイルから文字を抽出するToolとか方法(batファイルを作る等?)はありませんでしょうか。 参考となるサイト等でもわかればなんとかします。 どうかお願いいたします。

  • 日付時刻編集して保存する方法教えてください。

    編集前  A年月日     B時間   C     D 2010.12.24      16:30    13.8    11.9 2010.12.24      16:60    14.2    12.3 2010.12.23      15:30    15.6    13.4 2010.12.23      15:30    16.2    14.2 下に800行続きます。 編集後 A日+時間     B       C    241630        138     119 241660        142     123 231530        156     134 231530        162     142 A列に日と時間を記号なしで数字のみで書き出す ABCD4列をABC3列にする。 BCの数字を10倍する 以上 このような編集は可能でしょうか? 上のカンマ区切りのテキストCSVファイルを編集するマクロというのは出来ますでしょうか。 初心者なのでそのまま使える記述を教えていただけたらとても助かります。 どうぞよろしくお願いします。<(_ _ ;)>

  • 重複データの削除方法

    data.datのログ形式 先頭、最後は半角カンマ  fgetcsv でdata.datを1行単位で読み込み ,a.hoge@hoge.com, ,ba.hoge@hoge.com, ,c.hoge@hoge.com, ,a.hoge@hoge.com, ,b.hoge@hoge.com, ,c.hoge@hoge.com, 重複した行の1行を削除して詰めてdata.datを上書きする方法の簡単な方法をお願いします。 参考になるようなサンプルコードを書いていただけるとありがたいですが、自力で勉強もしたいので、ヒントや参考サイトがあれば教えて下さい。

    • 締切済み
    • PHP
  • エクセル関数で質問です。

    初心者なので上手く説明はできませんが宜しくお願いします。 例えば 列はA~E 行は1~50で1~50までの数字があるとします。 質問ですが例えば2行目AB列に5と10が並んでるとします他にも同じく5と10が同じように違う行で 並んでいるのですがそのカウント仕方が解りません数字1つの場合はカウントの仕方は解るのですが 数字が2つ3つ重なる数字のカウントの仕方が解りませんので教えて下さい