【问题标题】:PHP : Pop the last line of huge text log filePHP:弹出大文本日志文件的最后一行
【发布时间】:2012-08-29 09:59:14
【问题描述】:

我有一个巨大的日志文件(大约 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);

【问题讨论】:

  • 我相信这是使用 shell 的正确方法,只需确保转义输入以避免命令行注入
  • 同意。我添加了escapeshellarg()。但是如何有效地从文件中删除最后一行?
  • 出于好奇:为什么需要在 PHP 中这样做?为什么你不能在 shell 中做呢?
  • 我们需要处理网页中的日志。当然PHP也可以调用shell脚本。

标签: php


【解决方案1】:

从大文件中获取和删除最后一行的最快方法是:

  1. 打开文件进行写入
  2. 坚持到底
  3. 向后寻找一些任意缓冲区长度(比如说 1K)并读取数据以填充缓冲区
  4. 使用类似 strrpos 的内容向后搜索缓冲区,直到找到行尾标记¹
  5. 如果您没有找到 EOL,请转到第 3 步并重复
  6. 如果您确实找到了 EOL,您将根据缓冲区中的位置和从缓冲区读取的偏移量知道它发生的文件偏移量
  7. 通过查找该偏移量并读取直到文件末尾来获取最后一行²
  8. 调用ftruncate截断文件中从找到的行尾开始的部分

¹ 支持所有\n\r\r\n 会使事情复杂化 小的;特别是对于后者,它总是可能发生跨越 跨两个缓冲区,因此您必须明确注意这一点。

² 这不是绝对必要的,因为您将要访问的所有数据 read 已经通过了缓冲区,所以你可以保留一个 复制并节省了此操作的成本。在实践中虽然最后 线路不会太长,这样更方便 重新读取整个内容(C 运行时和/或 OS 文件系统缓存可能会使这个 无论如何都快得愚蠢)。

这是任何程序都必须做的。如果您决定通过将前七个步骤卸载到tail 之类的外部实用程序来“作弊”,您可以通过调用ftruncate 从文件中删除该行,但是:计算时要小心如果您不希望在文件中留下尾随的行尾字符,则截断的偏移量。

【讨论】:

  • 只是换个方式问,从文件中“弹出”第一行是否更容易?
  • @ShivanRaptor:弹出会非常慢,因为您必须读取所有将要“停留”的数据并从偏移量 0 开始重新写入。所有这些。
  • 我将带有您答案的代码写入我的问题。你能看看代码是否有效吗?
  • @ShivanRaptor:不,那里有很多错误。应在阅读前立即进行搜索;您应该阅读尽可能多的内容(除非文件小于一个缓冲区); strrpos 而不是 strpos;这些函数不会返回-1,而是在找不到针时返回false;你用错误的论点打电话给ftell。开始修复:)
  • @ShivanRaptor:很抱歉,但不能像这样继续调试代码。自己尝试一下,如果有特定问题给您带来麻烦,请再问一个问题。
猜你喜欢
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 2019-07-26
相关资源
最近更新 更多