Прочитайте файл в обратном порядке, используя fseek
Как прочесть файл в обратном порядке, используя fseek?
Код
может быть полезен. должен быть кросс-платформенным и чистым php.
большое спасибо заранее
рассматривает
Йера
Ответы
Ответ 1
Вопрос задается с использованием 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, как я, вы можете распечатать вывод сразу, если это цель вашего script. Также легко ввести счетчик, если вы хотите ограничить количество строк.
$linesToShow = 3;
$counter = 0;
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) {
// Rest of code from example. After $lines[] = $currentLine; add:
$counter++;
}
Ответ 2
Если вы все равно прочитаете весь файл, просто используйте file(), чтобы прочитать файл в виде массива (каждая строка каждый элемент в массиве), а затем используйте array_reverse(), чтобы перевернуть массив назад и пропустить его. Или просто сделайте обратный цикл, где вы начинаете в конце и уменьшаетесь в каждом цикле.
$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
echo $f."<br />";
}
Ответ 3
<?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";
Ответ 4
Чтобы полностью изменить файл:
$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" для обозначения конца строки. Таким образом, выше может не работать во всех случаях.
Ответ 5
Вы не можете fseek строчно, потому что вы не знаете, сколько строк осталось до их чтения.
Вы должны либо прочитать весь файл в списке строк, либо если файл слишком большой для этого, и вам нужны только последние строки, прочитайте фрагменты фиксированного размера с конца файла и реализуйте немного сложнее логика, которая обнаруживает линии из таких данных.
Ответ 6
Чтение всего файла в массив и реверсирование прекрасны, если файл не огромен.
Вы можете выполнить буферизированное чтение вашего файла с начала до следующего:
- установить buffer_size B - это должно быть больше, чем самая длинная ожидаемая строка, иначе вам понадобится некоторая логика для увеличения размера буфера, когда строки слишком длинны
- set offset = длина файла - buffer_size
- а смещение >= 0
- читать байты buffer_size из смещения
- прочитайте строку - она будет неполной, так как мы прыгнем в середину строки, поэтому мы хотим, чтобы следующий буфер, который мы читаем, заканчивался им. Установить смещение = смещение - buffer_size + длина строки
- отбросить эту строку, прочитать все следующие строки в массив и отменить их
- обработать этот массив, чтобы сделать все, что вы хотели сделать
Ответ 7
Этот код читает файл назад. Этот код игнорирует изменения при чтении, например 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);