SplFileObject将指针移动到上一行


SplFileObject move pointer to previous line

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

我认为以相反的顺序显示它会更合乎逻辑(顶部的最新错误(。要使用"正常"顺序,我只需要显示每一行并调用$file->next((;移动指针,但由于我正在以另一种方式进行操作,而且据我所知,还没有prev()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我只是一个被迫编写代码的设计师,所以我不太熟悉指针之类的东西。

来自PHP DOC

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();
    }
}

如果将来有人遇到这个问题,我会想出一个非常简单的解决方案:

//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

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

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

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