Как прочесть файл в обратном порядке, используя fseek?
Кодможет быть полезен. должен быть кросс-платформенным и чистым php.
большое спасибо заранее
рассматривает
Йера
Как прочесть файл в обратном порядке, используя fseek?
Кодможет быть полезен. должен быть кросс-платформенным и чистым php.
большое спасибо заранее
рассматривает
Йера
Вопрос задается с использованием 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++;
}
Если вы все равно прочитаете весь файл, просто используйте file(), чтобы прочитать файл в виде массива (каждая строка каждый элемент в массиве), а затем используйте 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($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" для обозначения конца строки. Таким образом, выше может не работать во всех случаях.
Вы не можете fseek строчно, потому что вы не знаете, сколько строк осталось до их чтения.
Вы должны либо прочитать весь файл в списке строк, либо если файл слишком большой для этого, и вам нужны только последние строки, прочитайте фрагменты фиксированного размера с конца файла и реализуйте немного сложнее логика, которая обнаруживает линии из таких данных.
Чтение всего файла в массив и реверсирование прекрасны, если файл не огромен.
Вы можете выполнить буферизированное чтение вашего файла с начала до следующего:
Этот код читает файл назад. Этот код игнорирует изменения при чтении, например 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);