Comment lire seulement 5 dernière ligne du fichier texte en PHP?

J'ai un fichier nommé "fichier.txt" il met à jour en y ajoutant des lignes.

Je le lis par ce code:

$fp = fopen("file.txt", "r");
$data = "";
while(!feof($fp))
{
$data .= fgets($fp, 4096);
}
echo $data;

Et un grand nombre de lignes apparaît. Je veux juste faire écho aux 5 dernières lignes du fichier

Comment puis-je faire ça ?


Le file.txt est comme ceci:

11111111111111
22222222222

33333333333333
44444444444

55555555555555
66666666666
25
demandé sur kenorb 2010-06-03 01:09:18

17 réponses

Code non testé, mais devrait fonctionner:

$file = file("filename.txt");
for ($i = max(0, count($file)-6); $i < count($file); $i++) {
  echo $file[$i] . "\n";
}

Appelant {[1] } gérera le fichier étant inférieur à 6 lignes.

18
répondu Maerlyn 2016-02-15 23:30:46

Pour un fichier volumineux, lire toutes les lignes dans un tableau avec file () est un peu inutile. Voici comment vous pouvez lire le fichier et maintenir un tampon des 5 dernières lignes:

$lines=array();
$fp = fopen("file.txt", "r");
while(!feof($fp))
{
   $line = fgets($fp, 4096);
   array_push($lines, $line);
   if (count($lines)>5)
       array_shift($lines);
}
fclose($fp);

Vous pouvez optimiser cela un peu plus avec quelques heuristiques sur la longueur de ligne probable en cherchant une position, disons, environ 10 lignes de la fin, et en remontant plus loin si cela ne donne pas 5 lignes. Voici une implémentation simple qui démontre que:

//how many lines?
$linecount=5;

//what's a typical line length?
$length=40;

//which file?
$file="test.txt";

//we double the offset factor on each iteration
//if our first guess at the file offset doesn't
//yield $linecount lines
$offset_factor=1;


$bytes=filesize($file);

$fp = fopen($file, "r") or die("Can't open $file");


$complete=false;
while (!$complete)
{
    //seek to a position close to end of file
    $offset = $linecount * $length * $offset_factor;
    fseek($fp, -$offset, SEEK_END);


    //we might seek mid-line, so read partial line
    //if our offset means we're reading the whole file, 
    //we don't skip...
    if ($offset<$bytes)
        fgets($fp);

    //read all following lines, store last x
    $lines=array();
    while(!feof($fp))
    {
        $line = fgets($fp);
        array_push($lines, $line);
        if (count($lines)>$linecount)
        {
            array_shift($lines);
            $complete=true;
        }
    }

    //if we read the whole file, we're done, even if we
    //don't have enough lines
    if ($offset>=$bytes)
        $complete=true;
    else
        $offset_factor*=2; //otherwise let's seek even further back

}
fclose($fp);

var_dump($lines);
39
répondu Paul Dixon 2010-06-06 15:43:22
function ReadFromEndByLine($filename,$lines)
{

        /* freely customisable number of lines read per time*/
        $bufferlength = 5000;

        $handle = @fopen($filename, "r");
        if (!$handle) {
                echo "Error: can't find or open $filename<br/>\n";
                return -1;
        }

        /*get the file size with a trick*/
        fseek($handle, 0, SEEK_END);
        $filesize = ftell($handle);

        /*don't want to get past the start-of-file*/
        $position= - min($bufferlength,$filesize);

        while ($lines > 0) {

                if ($err=fseek($handle,$position,SEEK_END)) {  /* should not happen but it's better if we check it*/
                        echo "Error $err: something went wrong<br/>\n";
                        fclose($handle);
                        return $lines;
                }

                /* big read*/
                $buffer = fread($handle,$bufferlength);

                /* small split*/
                $tmp = explode("\n",$buffer);

                /*previous read could have stored a partial line in $aliq*/
                if ($aliq != "") {

                                /*concatenate current last line with the piece left from the previous read*/
                                $tmp[count($tmp)-1].=$aliq;
                }

                /*drop first line because it may not be complete*/
                $aliq = array_shift($tmp);

                $read = count($tmp);
                if ( $read >= $lines ) {   /*have read too much!*/

                        $tmp2 = array_slice($tmp,$read-$n);
                        /* merge it with the array which will be returned by the function*/
                        $lines = array_merge($tmp2,$lines);

                        /* break the cycle*/
                        $lines = 0;
                } elseif (-$position >= $filesize) {  /* haven't read enough but arrived at the start of file*/

                        //get back $aliq which contains the very first line of the file
                        $lines = array_merge($aliq,$tmp,$lines);

                        //force it to stop reading
                        $lines = 0;

                } else {              /*continue reading...*/

                        //add the freshly grabbed lines on top of the others
                        $lines = array_merge($tmp,$lines);

                        $lines -= $read;

                        //next time we want to read another block
                        $position -= $bufferlength;

                        //don't want to get past the start of file
                        $position = max($position, -$filesize);
                }
        }
        fclose($handle);

        return $lines;
}

Ce sera rapide pour les fichiers plus volumineux mais beaucoup de code pour une tâche simple, s'il y a des fichiers volumineux, utilisez ceci

ReadFromEndByLine('monfichier.txt', 6);

13
répondu RobertPitt 2010-06-02 21:36:01

Si vous êtes sur un système linux, vous pouvez le faire:

$lines = `tail -5 /path/to/file.txt`;

Sinon, vous devrez compter les lignes et prendre les 5 derniers, quelque chose comme:

$all_lines = file('file.txt');
$last_5 = array_slice($all_lines , -5);
10
répondu Rob 2010-06-02 21:27:33

C'est une question d'entrevue. Voici ce que j'ai écrit l'année dernière quand on m'a posé cette question. Rappelez-vous que le code que vous obtenez sur Stack Overflow est autorisé avec Creative Commons Share-Alike avec attribution required.

<?php

/**
 * Demonstrate an efficient way to search the last 100 lines of a file
 * containing roughly ten million lines for a sample string. This should
 * function without having to process each line of the file (and without making
 * use of the “tail” command or any external system commands). 
 */

$filename = '/opt/local/apache2/logs/karwin-access_log';
$searchString = 'index.php';
$numLines = 100;
$maxLineLength = 200;

$fp = fopen($filename, 'r');

$data = fseek($fp, -($numLines * $maxLineLength), SEEK_END);

$lines = array();
while (!feof($fp)) {
  $lines[] = fgets($fp);
}

$c = count($lines);
$i = $c >= $numLines? $c-$numLines: 0;
for (; $i<$c; ++$i) {
  if ($pos = strpos($lines[$i], $searchString)) {
    echo $lines[$i];
  }
}

Cette solution fait une hypothèse sur la longueur de ligne maximale. L'intervieweur m'a demandé comment je résoudrais le problème si Je ne pouvais pas faire cette hypothèse, et a dû accueillir des lignes qui étaient potentiellement plus longues que tout longueur max que j'ai choisi.

Je lui ai dit que tout projet de logiciel doit faire certaines hypothèses, mais je pourrais tester si $c était inférieur au nombre de lignes souhaité, et si ce n'est pas le cas, fseek() revenir progressivement (doubler à chaque fois) jusqu'à ce que nous obtenions assez de lignes.

7
répondu Bill Karwin 2010-06-02 21:27:44

Cela n'utilise pas file() donc ce sera plus efficace pour les fichiers énormes;

<?php
function read_backward_line($filename, $lines, $revers = false)
{
    $offset = -1;
    $c = '';
    $read = '';
    $i = 0;
    $fp = @fopen($filename, "r");
    while( $lines && fseek($fp, $offset, SEEK_END) >= 0 ) {
        $c = fgetc($fp);
        if($c == "\n" || $c == "\r"){
            $lines--;
            if( $revers ){
                $read[$i] = strrev($read[$i]);
                $i++;
            }
        }
        if( $revers ) $read[$i] .= $c;
        else $read .= $c;
        $offset--;
    }
    fclose ($fp);
    if( $revers ){
        if($read[$i] == "\n" || $read[$i] == "\r")
            array_pop($read);
        else $read[$i] = strrev($read[$i]);
        return implode('',$read);
    }
    return strrev(rtrim($read,"\n\r"));
}
//if $revers=false function return->
//line 1000: i am line of 1000
//line 1001: and i am line of 1001
//line 1002: and i am last line
//but if $revers=true function return->
//line 1002: and i am last line
//line 1001: and i am line of 1001
//line 1000: i am line of 1000
?>
5
répondu dukevin 2012-05-23 00:38:03

La plupart des options ici supposent de lire le fichier dans la mémoire, puis de travailler avec des lignes. Ce ne serait pas une bonne idée, si le fichier est trop grand

Je pense que le meilleur moyen est d'utiliser un utilitaire OS, comme 'tail' dans unix.

exec('tail -3 /logs/reports/2017/02-15/173606-arachni-2415.log', $output);
echo $output;

// 2017-02-15 18:03:25 [*] Path Traversal: Analyzing response ...
// 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ...
// 2017-02-15 18:03:27 [*] Path Traversal: Analyzing response ...
4
répondu Anton 2017-02-15 18:08:37

La fonction PHP file () lit le fichier entier dans un tableau. Cette solution nécessite le moins de typage:

$data = array_slice(file('file.txt'), -5);

foreach ($data as $line) {
    echo $line;
}
3
répondu Lotus Notes 2010-06-02 21:21:52

Ouvrir des fichiers volumineux avec file() peut générer un grand tableau, en réservant une partie considérable de la mémoire.

Vous pouvez réduire le coût de la mémoire avec SplFileObject car il parcourt chaque ligne.

Utiliser le seek méthode (de seekableiterator) pour récupérer la dernière ligne. Vous devez ensuite soustraire la valeur de la clé actuelle de 5.

Pour obtenir la dernière ligne, utilisez PHP_INT_MAX. (Oui, c'est une solution de contournement.)

$file = new SplFileObject('large_file.txt', 'r');

$file->seek(PHP_INT_MAX);

$last_line = $file->key();

$lines = new LimitIterator($file, $last_line - 5, $last_line);

print_r(iterator_to_array($lines));
3
répondu Wallace Maxters 2016-03-15 18:08:07

Cette fonction fonctionnera pour les fichiers très volumineux de moins de 4 Go. La vitesse vient de la lecture d'un gros morceau de données au lieu de 1 octet à la fois et des lignes de comptage.

// Will seek backwards $n lines from the current position
function seekLineBackFast($fh, $n = 1){
    $pos = ftell($fh);
    if ($pos == 0)
        return false;

    $posAtStart = $pos;

    $readSize = 2048*2;
    $pos = ftell($fh);
    if(!$pos){
            fseek($fh, 0, SEEK_SET);
            return false;
    }

    // we want to seek 1 line before the line we want.
    // so that we can start at the very beginning of the line
    while ($n >= 0) {
        if($pos == 0)
                    break;
            $pos -= $readSize;
            if($pos <= 0){
                    $pos = 0;
            }

            // fseek returns 0 on success and -1 on error
            if(fseek($fh, $pos, SEEK_SET)==-1){
                    fseek($fh, 0, SEEK_SET);
                    break;
            }
            $data = fread($fh, $readSize);
            $count = substr_count($data, "\n");
            $n -= $count;

            if($n < 0)
                    break;
    }
    fseek($fh, $pos, SEEK_SET);
    // we may have seeked too far back
    // so we read one line at a time forward
    while($n < 0){
            fgets($fh);
            $n++;
    }
    // just in case?
    $pos = ftell($fh);
    if(!$pos)
        fseek($fh, 0, SEEK_SET);

    // check that we have indeed gone back
    if ($pos >= $posAtStart)
        return false;

    return $pos;
}

Après avoir exécuté la fonction ci-dessus, vous pouvez simplement faire fgets() dans une boucle pour lire chaque ligne à la fois à partir de $FH.

2
répondu over_optimistic 2016-10-30 21:33:43

Vous pouvez utiliser ma petite bibliothèque d'assistance (2 fonctions)

Https://github.com/jasir/file-helpers

Ensuite, utilisez simplement:

//read last 5 lines
$lines = \jasir\FileHelpers\FileHelpers::readLastLines($pathToFile, 5);
1
répondu jasir 2013-01-20 01:11:31

J'ai testé celui-ci. Il fonctionne pour moi.

function getlast($filename,$linenum_to_read,$linelength){

   // this function takes 3 arguments;


   if (!$linelength){ $linelength = 600;}
$f = fopen($filename, 'r');
$linenum = filesize($filename)/$linelength;

    for ($i=1; $i<=($linenum-$linenum_to_read);$i++) {
    $data = fread($f,$linelength);
    }
echo "<pre>";       
    for ($j=1; $j<=$linenum_to_read+1;$j++) {
    echo fread($f,$linelength);
    }

echo "</pre><hr />The filesize is:".filesize("$filename");
}

getlast("file.txt",6,230);


?>
0
répondu Kaibo 2010-11-29 05:00:43

Moins de ram, et les sorties bien. Je suis D'accord avec Paul Dixon...

$lines=array();
$fp = fopen("userlog.txt", "r");
while(!feof($fp))
{
 $line = fgets($fp, 4096);
 array_push($lines, $line);
 if (count($lines)>25)
   array_shift($lines);
}
fclose($fp);

while ($a <= 10) {
$a++;
echo "<br>".$lines[$a];
}
0
répondu Mikeys4u 2014-10-07 13:51:18
$dosya = "../dosya.txt";
$array = explode("\n", file_get_contents($dosya));
$reversed = array_reverse($array);
for($x = 0; $x < 6; $x++) 
{
    echo $reversed[$x];
}
0
répondu Muharrem Armağan Onur Kızıldağ 2015-11-19 21:55:48

, Voici ma solution:

/**
 *
 * Reads N lines from a file
 *
 * @param type $file       path
 * @param type $maxLines   Count of lines to read
 * @param type $reverse    set to true if result should be reversed.
 * @return string
 */
public function readLinesFromFile($file, $maxLines, $reverse=false)
{
    $lines = file($file);

    if ($reverse) {
        $lines = array_reverse($lines);
    }

    $tmpArr = array();

    if ($maxLines > count($lines))
        exit("\$maxLines ist größer als die Anzahl der Zeilen in der Datei.");

    for ($i=0; $i < $maxLines; $i++) {
        array_push($tmpArr, $lines[$i]);
    }

    if ($reverse) {
        $tmpArr = array_reverse($tmpArr);
    }

    $out = "";
    for ($i=0; $i < $maxLines; $i++) {
        $out .= $tmpArr[$i] . "</br>";
    }

    return $out;
}
0
répondu Black 2018-07-18 12:20:08

Si vos lignes sont séparées par un CR ou un LF, vous essayez d'exploser votre variable $ data:

$lines = explode("\n", $data);

$ lines devrait finir par être un tableau et vous pouvez calculer le nombre d'enregistrements en utilisant sizeof() et obtenir simplement les 5 derniers.

-2
répondu Alistair 2011-11-13 17:03:23

Ceci est lu dernière ligne 10 du fichier texte

$data = array_slice(file('logs.txt'),10);

    foreach ($data as $line) 

    {

        echo $line."<br/>";
    }
-2
répondu santosh 2014-03-13 10:50:02