巨大なログファイル(500MBくらい)をphpだけで処理しようとしたら苦戦しまくったのでまとめました。
やりたい事 : ログファイルの最終行の内容を取得する
失敗すると : Allowed memory size of 536870912 bytes exhausted (tried to allocate 688430032 bytes)
こうなります。。。
最初に完成形
php
$file_handle = fopen($file, "r");
fseek($file_handle, -2000, SEEK_END); //ファイルポインタを終端から2000バイト前に設定
ob_start();
fpassthru($file_handle); //ファイルポインタの位置からファイルの終端まで出力する
$re = ob_get_clean();
fclose($file_handle);
$lines = explode(PHP_EOL, $re, -1);
echo $lines[count($lines)-1];
エラー対応とかは省いています。必要最低限だとこうなりました。
ファイルのポインタを終端から2000バイト前にずらしたあとに、そこから終端までを出力。
その内容をバッファリングして、配列を最後にexplodeしてます。
失敗例
file — ファイル全体を読み込んで配列に格納する
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
echo $lines[count($lines)-1];
全体を読み込むので重くて駄目でした。
fgets — ファイルポインタから 1 行取得する
$file_handle = fopen($file, "a+"); //ファイルポインタをファイルの終端に設定
$re = fgets($file_handle);
fseek($file_handle, -2000, SEEK_END); //ファイルポインタを終端から2000バイト前に設定
$re = fgets($file_handle);
ファイルポインタの位置を変えても結局、そこから1行単位でしか取得できないのでこちらも駄目でした。
SplFileObject::current — ファイルの現在の行を取得する
$splFileObject = new SplFileObject($file);
$splFileObject->fseek($splFileObject, -1, SEEK_END); //ファイルポインタを終端から1バイトに設定
#$splFileObject->seek(ファイルの行数); //phpのみでファイルの行数が取得できればいけそう。
echo $splFileObject->current();
かなりいけそうだと思いましたがこれも駄目でした。
ファイルの行数をphpのみで取得する方法(fopen,feof,whileとか使うの以外で)が分からず、諦めました。
まとめ
サイズの大きいファイルをphpで扱うときはポインタをうまく操作して
任意の場所だけ読むようにしないと遅くて処理が落ちる。
ファイルの行数をphpで取得する方法はexec('wc -l '.$file);
とかがいいのでしょうか。
どなたか分かる方いましたら教えてほしいです。