14

fseek を使用してファイルを 1 行ずつ逆方向に読み取るにはどうすればよいですか?

コードが役立ちます。クロス プラットフォームおよび純粋な php である必要があります。

よろしくお願いします

よろしく

ジェラ

4

10 に答える 10

23

とにかくファイル全体を読み取る場合は、file()を使用してファイルを配列として読み取り (各行は配列内の各要素です)、次にarray_reverse()を使用して配列を逆方向に反転し、それをループします。 . または、最後から開始して各ループでデクリメントする逆の for ループを実行します。

$file = file("test.txt");
$file = array_reverse($file);
foreach($file as $f){
    echo $f."<br />";
}
于 2010-07-13T06:20:50.123 に答える
19

質問は 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++;
}
于 2013-02-22T05:48:37.770 に答える
17
<?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";
于 2012-05-08T07:58:49.360 に答える
8

ファイルを完全にリバースするには:

$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" を使用して行末を示しました。したがって、上記はすべての場合に機能するとは限りません。

于 2010-07-13T07:05:55.113 に答える
6

行を読むまで行の長さがわからないため、行ごとに検索することはできません。

ファイル全体を行のリストに読み込むか、ファイルが大きすぎて最後の行だけが必要な場合は、ファイルの末尾から固定サイズのチャンクを読み取り、検出するもう少し複雑なロジックを実装する必要がありますそのようなデータからの行。

于 2010-07-13T06:13:05.190 に答える
2

ファイルが巨大でない限り、ファイル全体を配列に読み込んで反転しても問題ありません。

次のような方法で、ファイルのバッファリングされた読み取りを後ろから前に実行できます。

  • buffer_size B を確立します - これは予想される最長の行よりも長くする必要があります。そうしないと、行が長すぎる場合にバッファー サイズを大きくするためのロジックが必要になります。
  • 設定オフセット = ファイル長 - buffer_size
  • オフセット>=0の間
    • オフセットから buffer_size バイトを読み取る
    • 行の読み取り - 行の途中にジャンプしたため不完全になるため、次に読み取るバッファーがその行で終わることを確認したいと考えています。オフセットを設定 = オフセット - buffer_size + 行の長さ
    • その行を破棄し、後続のすべての行を配列に読み取り、それらを逆にします
    • この配列を処理して、やりたいことを何でもします
于 2010-07-13T07:40:30.677 に答える
0

このコードは、ファイルを逆方向​​に読み取ります。このコードは、読み込み時の変更を無視します。

$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);
于 2015-01-10T21:47:50.403 に答える
0

これは、ファイルから行を逆順に読み取る 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;
            }
        }
    }
?>
于 2020-11-15T10:32:21.943 に答える