Читать файл построчно, используя fseek - PullRequest
13 голосов
/ 13 июля 2010

Как мне прочитать файл обратно построчно, используя fseek?

код может быть полезным. должен быть кроссплатформенным и чистым php.

большое спасибо заранее

привет

Jera

Ответы [ 7 ]

21 голосов
/ 13 июля 2010

Если вы собираетесь прочитать весь файл в любом случае, просто используйте file () , чтобы прочитать файл как массив (каждая строка - каждый элемент в массиве), а затем используйте array_reverse () чтобы перевернуть массив назад и пройти через него. Или просто сделайте обратный цикл for, где вы начинаете с конца и уменьшаете в каждом цикле.

$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
    echo $f."<br />";
}
18 голосов
/ 22 февраля 2013

Вопрос задается с помощью fseek, поэтому можно только предполагать, что производительность - это проблема, а file () - не решение. Вот простой подход с использованием fseek:

Мой файл.txt

#file.txt
Line 1
Line 2
Line 3
Line 4
Line 5

И код:

<?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);

Выход:

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"
}

Вам не нужно добавлять к массиву $ lines, как я, вы можете сразу распечатать вывод, если это является целью вашего скрипта. Также легко ввести счетчик, если вы хотите ограничить количество строк.

$linesToShow = 3;
$counter = 0;
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) {
   // Rest of code from example. After $lines[] = $currentLine; add:
   $counter++;
}
15 голосов
/ 08 мая 2012
<?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";
8 голосов
/ 13 июля 2010

Чтобы полностью перевернуть файл:

$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);

Действительно, у Джонатана Куна лучший ответИМХО выше.Единственный случай, когда вы не используете его ответ, о котором я знаю, это если file или подобные функции отключены через php.ini, но администратор забыл о fseek, или при открытии большого файла просто получите последние несколько строкволшебным образом сохранит память таким образом.

Примечание: обработка ошибок не включена.И PHP_EOL не сотрудничал, поэтому я использовал "\ n" для обозначения конца строки.Таким образом, выше может работать не во всех случаях.

6 голосов
/ 13 июля 2010

Вы не можете искать строку за строкой, потому что вы не знаете, какой длины строки, пока не прочитаете их.

Вы должны либо прочитать весь файл в список строк, либо файл слишкомдля этого достаточно, и вам нужны только последние строки, чтение фрагментов фиксированного размера из конца файла и реализация немного более сложной логики, которая обнаруживает строки из таких данных.

2 голосов
/ 13 июля 2010

Чтение всего файла в массив и реверсирование это нормально, если файл не огромен.

Вы можете выполнить буферизованное чтение вашего файла задом наперед с помощью чего-то вроде:

  • установить параметр buffer_size B - он должен быть длиннее самой длинной ожидаемой строки, в противном случае вам потребуется некоторая логика для увеличения размера буфера, если строки слишком длинные
  • установить смещение = длина файла - размер_буфера
  • при смещении> = 0
    • чтение байтов buffer_size из смещения
    • читать строку - она ​​будет неполной, так как мы прыгнули в середину строки, поэтому мы хотим, чтобы буфер следующий , который мы читали, заканчивался этим. Установить смещение = смещение - размер_буфера + длина строки
    • отбросить эту строку, прочитать все последующие строки в массив и обратить их обратно
    • обработать этот массив, чтобы сделать все, что вы хотели
0 голосов
/ 11 января 2015

Этот код читает файл в обратном направлении.В этом коде игнорируются изменения при чтении, например, новые строки apache access.log при сжатии.

$f = fopen('FILE', 'r');

fseek($f, 0, SEEK_END);

$pos = ftell($f);
$pos--;

while ($pos > 0) {
    $chr = fgetc($f);
    $pos --;

    fseek($f, $pos);

    if ($chr == PHP_EOL) {
        YOUR_OWN_FUNCTION($rivi);
        $rivi = NULL;
        continue;
    }

    $rivi = $chr.$rivi;
}

fclose($f);
...