2

所以我正在为自己做一个小实验,一个脚本来读取 php 错误的日志文件(使用 SplFileObject)并在浏览器上格式化输出。

我认为以相反的顺序显示它会更符合逻辑(最新的错误在顶部)。要使用“正常”顺序,我只需显示每一行并调用 $file->next(); 移动指针,但是当我以相反的方式进行操作时,据我所知没有prev()or方法,我发现的唯一方法是使用:previous()seek()

for($i = $lines_total - $start_at; $i > $lines_total - $start_at - $lines_to_get; $i--){
    $content->seek($i);
    $data = $content->current();
    if(empty($data)){
        continue;
    }
}

但这非常慢(16mb 文件大约需要 7 秒)。如果我按照正常顺序进行操作,那是即时的。

有谁知道任何方法?或者我想做的是疯了?xD 我只是一个被迫编码的设计师,所以我对指针和类似的东西不是很熟悉。

4

3 回答 3

2

来自 PHP 文档

prev - 倒回内部数组指针
prev()的行为就像next(),除了它将内部数组指针倒回一个位置而不是前进它。

如您所见,它们仅适用于数组而不是文件指针....您只能像这样使用它

$file = new SplFileObject("log.txt", "r");
$file  = iterator_to_array($file);

echo current($file), PHP_EOL;
echo next($file), PHP_EOL;
echo prev($file), PHP_EOL;

如果你想移动到下一行,你可以尝试使用SplFileObject::ftell来获取上一个位置,然后使用SplFileObject::fseek来实现你的反向...

例子

$file = new ReverseableSplFileObject("log.txt", "r");
foreach ( $file as $c ) {
    echo $c, PHP_EOL;
}
echo $file->prev(), PHP_EOL;
echo $file->prev(), PHP_EOL;
echo $file->prev(), PHP_EOL;

输出

A
B
C
C
B
A

修改类

class ReverseableSplFileObject extends SplFileObject {
    private $pos = array();

    function current() {
        return trim(parent::current());
    }

    function next() {
        $this->eof() or $this->pos[] = $this->ftell();
        return parent::next();
    }

    function prev() {
        if (empty($this->pos)) {
            $this->fseek(0);
        } else {
            $this->fseek(array_pop($this->pos));
        }
        return $this->current();
    }
}
于 2013-02-20T17:00:50.610 回答
2

万一以后有人遇到这个问题,我想出了一个非常简单的解决方案:

//get to the last position of the file and get the pointer position.
$content->seek($content->getSize());
$lines_total = $content->key();
$byte = $content->ftell();

//all the line in the php error log starts like: [21-Feb-2013 22:34:53 UTC] so...
$pattern = '/^\[.*\]/';

for(...){
//get the current output to preg_match it
    $data = $content->current();

//go backward each time it doesnt match a line's start
    while ( preg_match( $pattern, $data ) === 0 ){
    $byte--;
    $content->fseek($byte);
    $data = $content->current();
    }

//go backward 1 more position to start the next loop
    $byte--;
    $content->fseek($byte);
}

希望有一天这对某人有所帮助xD

于 2013-02-21T13:08:58.000 回答
0

这是一个老问题,但使用 SplFileObj 最简单的方法是使用密钥并查找,

public function prev(){
    $key = $this->_splFileObj->key();
    if( $key ){
        --$key;
    }
    $this->_splFileObj->seek($key);
}

假设此方法位于 SplFileObj 的包装器中,其属性为_splFileObj.

于 2015-01-14T19:05:26.613 回答