0

巨大なログファイル(約1,000,000行)があります。最後の行を取得し、PHPを使用してファイルから削除したいと思います。そうするための最も速い方法は何ですか?

私は試した:

$logfile = escapeshellarg("/path/to/logfile");
$lastline = `tail -n 1 "$logfile"`; // obtained the last line

上記のアプローチは十分に効率的ですか?そして、ファイルから最後の行を削除する方法は?

以下のジョンの答えから、ここにコードがあります:

$buffer_size = 1000;
$fh = fopen("/path/to/logfile", "r+");
fseek($fh, -$buffer_size, SEEK_END);
$content = fgets($fh, 100);
while(strrpos($content, PHP_EOL) != false) {
  fseek($fh, -$buffer_size); // move backward for extra -1000
  $content = fgets($fh, $buffer_size);
}
$pos_last_eol = strrpos($content, PHP_EOL);
fseek($fh, $pos_last_eol); // seek to that position
ftruncate($fh, ftell($fh));
fclose($fh);
4

1 に答える 1

2

大きなファイルから最後の行を取得して削除する最も速い方法は次のとおりです。

  1. 書き込み用にファイルを開きます
  2. 最後まで探す
  3. 任意のバッファ長を逆方向にシークし(たとえば1K)、データを読み取ってバッファを埋めます
  4. strrpos行末マーカーが見つかるまで、次のようなものでバッファを逆方向に検索します¹
  5. EOLが見つからない場合は、手順3に進んで繰り返します。
  6. EOLが見つかった場合は、バッファー内の位置とバッファーが読み取られたオフセットに基づいて、EOLが発生するファイルオフセットがわかります。
  7. そのオフセットを探し、ファイルの終わりまで読み取ることによって最後の行を取得します²
  8. ftruncate見つかった行末で始まるファイルの部分を切り取るために呼び出します

¹、、のすべてをサポートすると、\n状況が少し複雑になります。特に後者の場合、常に2つのバッファにまたがる可能性があるため、明示的に注意する必要があります。\r\r\n

²これは、読み取るすべてのデータがすでにバッファーを通過しているため、厳密には必要ありません。したがって、コピーを保持して、この操作のコストを節約できたはずです。実際には、最後の行は長すぎないので、全体を読み直す方が便利です(CランタイムやOSファイルシステムキャッシュは、とにかくこれをばかげて速くするでしょう)。

これは、どのプログラムでも実行する必要があることです。最初の7つのステップを外部ユーティリティにオフロードして「チート」することにした場合は、を1回呼び出すだけtailでファイルから行を削除できますが、ftruncate終了したくない場合は、切り捨てるオフセットを計算するときに注意してください。ファイル内の行末文字。

于 2012-08-29T10:07:05.977 に答える