PHP:从大文本文件的末尾检索行

时间:2012-06-17 01:10:49

标签: php

我已经搜索了很长一段时间的答案,并且没有找到任何可以正常工作的答案。

我有大约100MB行文本的日志文件,其中一些文件大小达140,000。 使用PHP,我正在尝试获取文件的最后500行。

我如何获得500行?对于大多数函数,文件被读入内存,这不是一个合理的案例。我最好远离执行系统命令。

3 个答案:

答案 0 :(得分:6)

如果你在'nix机器上,你应该能够使用shell转义和工具'tail'。 已经有一段时间了,但是这样的事情:

$lastLines = `tail -n 500`;

注意使用刻度线,它在BASH或类似物中执行字符串并返回结果。

答案 1 :(得分:4)

我写了这个函数,这对我来说似乎很有效。它返回一行数组,就像file一样。如果您希望它返回file_get_contents之类的字符串,则只需将return语句更改为return implode('', array_reverse($lines));

function file_get_tail($filename, $num_lines = 10){

    $file = fopen($filename, "r");

    fseek($file, -1, SEEK_END);

    for ($line = 0, $lines = array(); $line < $num_lines && false !== ($char = fgetc($file));) {
        if($char === "\n"){
            if(isset($lines[$line])){
                $lines[$line][] = $char;
                $lines[$line] = implode('', array_reverse($lines[$line]));
                $line++;
            }
        }else
            $lines[$line][] = $char;
        fseek($file, -2, SEEK_CUR);
    }
    fclose($file);

    if($line < $num_lines)
        $lines[$line] = implode('', array_reverse($lines[$line]));

    return array_reverse($lines);
}

示例:

file_get_tail('filename.txt', 500);

答案 2 :(得分:3)

如果你想在PHP中这样做:

<?php
/**
  Read last N lines from file.

  @param $filename string  path to file. must support seeking
  @param $n        int     number of lines to get.

  @return array            up to $n lines of text
*/
function tail($filename, $n)
{
  $buffer_size = 1024;

  $fp = fopen($filename, 'r');
  if (!$fp) return array();

  fseek($fp, 0, SEEK_END);
  $pos = ftell($fp);

  $input = '';
  $line_count = 0;

  while ($line_count < $n + 1)
  {
    // read the previous block of input
    $read_size = $pos >= $buffer_size ? $buffer_size : $pos;
    fseek($fp, $pos - $read_size, SEEK_SET);

    // prepend the current block, and count the new lines
    $input = fread($fp, $read_size).$input;
    $line_count = substr_count(ltrim($input), "\n");

    // if $pos is == 0 we are at start of file
    $pos -= $read_size;
    if (!$pos) break;
  }

  fclose($fp);

  // return the last 50 lines found  

  return array_slice(explode("\n", rtrim($input)), -$n);
}

var_dump(tail('/var/log/syslog', 50));

这在很大程度上是未经测试的,但应该足以让您获得完全可行的解决方案。

缓冲区大小为1024,但可以更改为更大或更大。 (你甚至可以根据行长度的$ n *估计动态设置它。)这应该比逐字符寻找更好,尽管它确实意味着我们需要substr_count()来寻找新行。