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

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

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

Спасибо заранее

С уважением

Jera

Вопрос задается с использованием fseek, поэтому можно только предположить, что производительность является проблемой, а файл () не является решением. Вот простой подход с использованием 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++; } 

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

 $file = file("test.txt"); $file = array_reverse($file); foreach($file as $f){ echo $f."<br />"; } 
 <?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"; 

Чтобы полностью изменить файл:

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

Конечно, вам нужно разворот по очереди …


 $ 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") {
         // анализируем завершенную строку $ output [$ ln], если необходимо
         $ Пер ++;
         Продолжать;
         }
     $ output [$ ln] = $ char.  ((array_key_exists ($ ln, $ output))? $ output [$ ln]: '');
     }
 fclose ($ фл);
 print_r ($ выход);

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

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

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

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

Чтение всего файла в массив и реверсирование прекрасны, если файл не огромен.

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

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

Этот код читает файл назад. Этот код игнорирует изменения при чтении, например 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);