LoginSignup
5
6

More than 5 years have passed since last update.

phpでサイズの大きいファイルを操作する

Posted at

巨大なログファイル(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); とかがいいのでしょうか。
どなたか分かる方いましたら教えてほしいです。

5
6
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6