使用fseek逐行向后读取文件

Jer*_*Gwa 13 php io file fseek

如何使用fseek逐行向后读取文件?

代码可能会有所帮助.必须跨平台和纯PHP.

提前谢谢了

问候

杰拉

Jon*_*uhn 20

如果您要以任何方式读取整个文件,只需使用file()以数组形式读取文件(每行是数组中的每个元素)然后使用array_reverse()向后翻转数组并循环遍历.或者只是执行一个反向循环,从最后开始循环并减少每个循环.

$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
    echo $f."<br />";
}
Run Code Online (Sandbox Code Playgroud)

  • 嘿!如果您正在解析一个非常大的文件(数百万行),那么通过将整个文件加载到内存中,这将很快耗尽内存负载。这不是一个好主意! (2认同)

Jam*_*ieL 18

问题是要求使用fseek,因此只能假设性能是一个问题而file()不是解决方案.这是一个使用fseek的简单方法:

我的file.txt

#file.txt
Line 1
Line 2
Line 3
Line 4
Line 5
Run Code Online (Sandbox Code Playgroud)

和代码:

<?php

$fp = fopen('file.txt', 'r');

$pos = -2; // Skip final new line character (Set to -1 if not present)

$lines = array();
$currentLine = '';

while (-1 !== fseek($fp, $pos, SEEK_END)) {
    $char = fgetc($fp);
    if (PHP_EOL == $char) {
            $lines[] = $currentLine;
            $currentLine = '';
    } else {
            $currentLine = $char . $currentLine;
    }
    $pos--;
}

$lines[] = $currentLine; // Grab final line

var_dump($lines);
Run Code Online (Sandbox Code Playgroud)

输出:

array(5) {
   [0]=>
   string(6) "Line 5"
   [1]=>
   string(6) "Line 4"
   [2]=>
   string(6) "Line 3"
   [3]=>
   string(6) "Line 2"
   [4]=>
   string(6) "Line 1"
}
Run Code Online (Sandbox Code Playgroud)

您不必像我一样附加到$ lines数组,如果这是您脚本的目的,您可以立即打印输出.如果要限制行数,也很容易引入计数器.

$linesToShow = 3;
$counter = 0;
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) {
   // Rest of code from example. After $lines[] = $currentLine; add:
   $counter++;
}
Run Code Online (Sandbox Code Playgroud)


cha*_*101 16

<?php

class ReverseFile implements Iterator
{
    const BUFFER_SIZE = 4096;
    const SEPARATOR = "\n";

    public function __construct($filename)
    {
        $this->_fh = fopen($filename, 'r');
        $this->_filesize = filesize($filename);
        $this->_pos = -1;
        $this->_buffer = null;
        $this->_key = -1;
        $this->_value = null;
    }

    public function _read($size)
    {
        $this->_pos -= $size;
        fseek($this->_fh, $this->_pos);
        return fread($this->_fh, $size);
    }

    public function _readline()
    {
        $buffer =& $this->_buffer;
        while (true) {
            if ($this->_pos == 0) {
                return array_pop($buffer);
            }
            if (count($buffer) > 1) {
                return array_pop($buffer);
            }
            $buffer = explode(self::SEPARATOR, $this->_read(self::BUFFER_SIZE) . $buffer[0]);
        }
    }

    public function next()
    {
        ++$this->_key;
        $this->_value = $this->_readline();
    }

    public function rewind()
    {
        if ($this->_filesize > 0) {
            $this->_pos = $this->_filesize;
            $this->_value = null;
            $this->_key = -1;
            $this->_buffer = explode(self::SEPARATOR, $this->_read($this->_filesize % self::BUFFER_SIZE ?: self::BUFFER_SIZE));
            $this->next();
        }
    }

    public function key() { return $this->_key; }
    public function current() { return $this->_value; }
    public function valid() { return ! is_null($this->_value); }
}

$f = new ReverseFile(__FILE__);
foreach ($f as $line) echo $line, "\n";
Run Code Online (Sandbox Code Playgroud)


bob*_*yer 8

要完全反转文件:

$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $output = ''; fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
    $output .= fgetc($fl);
    }
fclose($fl);
print_r($output);

当然,你想要逐行逆转......


$fl = fopen("\some_file.txt", "r");
for($x_pos = 0, $ln = 0, $output = array(); fseek($fl, $x_pos, SEEK_END) !== -1; $x_pos--) {
    $char = fgetc($fl);
    if ($char === "\n") {
        // analyse completed line $output[$ln] if need be
        $ln++;
        continue;
        }
    $output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : '');
    }
fclose($fl);
print_r($output);

真的,Jonathan Kuhn上面有最好的答案恕我直言.唯一没有使用他的答案的情况是我知道是file通过php.ini禁用了函数,但管理员忘记了fseek,或者打开一个巨大的文件只是获取最后几行内容会神奇地保存以这种方式记忆.

注意:不包括错误处理.并且,PHP_EOL没有合作,所以我使用"\n"来表示行尾.因此,以上可能并不适用于所有情况.


zvo*_*one 6

你不能逐行查找,因为在你读它们之前你不知道这些行有多长.

您应该将整个文件读入一个行列表,或者如果文件太大而且您只需要最后一行,则从文件末尾读取固定大小的块并实现更复杂的逻辑来检测来自这些数据的行.