fseek を使用してファイルを 1 行ずつ逆方向に読み取るにはどうすればよいですか?
コードが役立ちます。クロス プラットフォームおよび純粋な php である必要があります。
よろしくお願いします
よろしく
ジェラ
とにかくファイル全体を読み取る場合は、file()を使用してファイルを配列として読み取り (各行は配列内の各要素です)、次にarray_reverse()を使用して配列を逆方向に反転し、それをループします。 . または、最後から開始して各ループでデクリメントする逆の for ループを実行します。
$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
echo $f."<br />";
}
質問は 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++;
}
<?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($出力);
もちろん、行ごとの反転が必要でした...
$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] を分析します $ln++; 継続する; } $output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : ''); } fclose($fl); print_r($出力);
本当に、ジョナサン・クーンは上記の私見で最高の答えを持っています. 私が知っている彼の答えを使用しない唯一のケースはfile
、管理者が fseek を忘れていた場合、または巨大なファイルを開くときにコンテンツの最後の数行を魔法のように保存する場合です。記憶はこう。
注: エラー処理は含まれていません。また、PHP_EOL は対応していないので、代わりに "\n" を使用して行末を示しました。したがって、上記はすべての場合に機能するとは限りません。
行を読むまで行の長さがわからないため、行ごとに検索することはできません。
ファイル全体を行のリストに読み込むか、ファイルが大きすぎて最後の行だけが必要な場合は、ファイルの末尾から固定サイズのチャンクを読み取り、検出するもう少し複雑なロジックを実装する必要がありますそのようなデータからの行。
ファイルが巨大でない限り、ファイル全体を配列に読み込んで反転しても問題ありません。
次のような方法で、ファイルのバッファリングされた読み取りを後ろから前に実行できます。
このコードは、ファイルを逆方向に読み取ります。このコードは、読み込み時の変更を無視します。
$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);
これは、ファイルから行を逆順に読み取る fgetsr() と呼ばれる fgets($fp) の置換 (っぽい) です。
このコードは逐語的であるため、(有名な最後の言葉) サーバー上のファイルにコピーして実行できるはずです。fopn() 呼び出しでファイル名を変更する必要があるかもしれませんが。
<?php
header('Content-Type: text/plain');
$fp = fopen('post.html', 'r');
while($line = fgetsr($fp)) {
echo $line;
}
// Read a line from the file but starting from the end
//
// @param $fp integer The file pointer
//
function fgetsr($fp)
{
// Make this variable persistent inside this function
static $seeked;
// The line buffer that will eventually be returned
$line = '';
// Initially seek to the end of the file
if (!$seeked) {
fseek($fp, -1, SEEK_END);
$seeked = true;
}
// Loop through all of the characters in the file
while(strlen($char = fgetc($fp)) {
// fgetc() advances that pointer so go back TWO places
// instead of one
fseek($fp, -2, SEEK_CUR);
//
// Check for a newline (LF). If a newline is found
// then break out of the function and return the
// line that's stored in the buffer.
//
// NB The first line in the file (ie the last to
// be read)has a special case
//
if (ftell($fp) <= 0) {
fseek($fp, 0, SEEK_SET);
$line = fgets($fp);
fseek($fp, 0, SEEK_SET);
return $line;
} else if ($char === "\n") {
$line = strrev($line);
return $line . "\n";
} else {
$line .= $char;
}
}
}
?>