web-dev-qa-db-fra.com

Lire la dernière ligne du fichier

J'ai rencontré un problème. J'ai un journal sur une boîte Linux dans laquelle est écrite la sortie de plusieurs processus en cours d'exécution. Ce fichier peut parfois devenir très volumineux et je dois lire la dernière ligne de ce fichier.

Le problème est que cette action sera appelée via une requête AJAX assez souvent et lorsque la taille du fichier de ce journal dépasse 5-6 Mo, ce n'est pas bon pour le serveur. Je pense donc que je avoir à lire la dernière ligne mais pas à lire tout le fichier et à le parcourir ou à le charger dans RAM car cela ne ferait que charger à mort ma boîte.

Existe-t-il une optimisation pour cette opération afin qu'elle s'exécute correctement et n'endommage pas le serveur ou ne tue pas Apache?

Une autre option que j'ai est de exec('tail -n 1 /path/to/log') mais ça ne sonne pas si bien.

Édition ultérieure: JE NE VEUX PAS placer le fichier dans RAM car il pourrait devenir énorme. fopen() n'est pas une option.

30

Cela devrait fonctionner:

$line = '';

$f = fopen('data.txt', 'r');
$cursor = -1;

fseek($f, $cursor, SEEK_END);
$char = fgetc($f);

/**
 * Trim trailing newline chars of the file
 */
while ($char === "\n" || $char === "\r") {
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

/**
 * Read until the start of file or first newline char
 */
while ($char !== false && $char !== "\n" && $char !== "\r") {
    /**
     * Prepend the new char
     */
    $line = $char . $line;
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

echo $line;
44
Ionuț G. Stan

Utilisez fseek . Vous recherchez la dernière position et la recherchez en arrière (utilisez ftell pour indiquer la position actuelle) jusqu'à ce que vous trouviez un "\ n".


$fp = fopen(".....");
fseek($fp, -1, SEEK_END); 
$pos = ftell($fp);
$LastLine = "";
// Loop backword util "\n" is found.
while((($C = fgetc($fp)) != "\n") && ($pos > 0)) {
    $LastLine = $C.$LastLine;
    fseek($fp, $pos--);
}

REMARQUE: je n'ai pas testé. Vous pourriez avoir besoin d'un ajustement.

MISE À JOUR: Merci Syntax Error pour signaler un fichier vide.

:-RÉ

UPDATE2: correction d'une autre erreur de syntaxe, point-virgule manquant à $LastLine = ""

17
NawaMan

Vous recherchez la fonction fseek . Il existe des exemples pratiques de lecture de la dernière ligne d'un fichier dans la section des commentaires.

5
slikts

Si vous connaissez la limite supérieure de la longueur de ligne, vous pouvez faire quelque chose comme ça.

$maxLength = 1024;
$fp = fopen('somefile.txt', 'r');
fseek($fp, -$maxLength , SEEK_END); 
$fewLines = explode("\n", fgets($fp, $maxLength));
$lastLine = $fewLines[count($fewLines) - 1];

En réponse à la modification: fopen acquiert juste un handle vers le fichier (c'est-à-dire assurez-vous qu'il existe, le processus a la permission, informe os qu'un processus utilise le fichier, etc ...). Dans cet exemple, seuls 1024 caractères du fichier seront lus en mémoire.

3
function readlastline() 
{ 
       $fp = @fopen("/dosmnt/LOGFILE.DAT", "r"); 
       $pos = -1; 
       $t = " "; 
       while ($t != "\n") { 
             fseek($fp, $pos, SEEK_END); 
             $t = fgetc($fp); 
             $pos = $pos - 1; 
       } 
       $t = fgets($fp); 
       fclose($fp); 
       return $t; 
} 

Source: http://forums.devshed.com/php-development-5/php-quick-way-to-read-last-line-156010.html

3
Daniel A. White

c'est le code de Ionuț G. Stan

j'ai un peu modifié votre code et en ai fait une fonction de réutilisation

function read_last_line ($file_path){



$line = '';

$f = fopen($file_path, 'r');
$cursor = -1;

fseek($f, $cursor, SEEK_END);
$char = fgetc($f);

/**
* Trim trailing newline chars of the file
*/
while ($char === "\n" || $char === "\r") {
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

/**
* Read until the start of file or first newline char
*/
while ($char !== false && $char !== "\n" && $char !== "\r") {
    /**
     * Prepend the new char
     */
    $line = $char . $line;
    fseek($f, $cursor--, SEEK_END);
    $char = fgetc($f);
}

return $line;
}

echo read_last_line ('log.txt');

vous obtiendrez cette dernière ligne

Votre problème ressemble à celui-ci

La meilleure approche pour éviter de charger tout le fichier en mémoire semble être:

$file = escapeshellarg($file); // for the security concious (should be everyone!)
$line = `tail -n 1 $file`;
1
James Goodwin

Serait-il possible d'optimiser cela de l'autre côté? Si c'est le cas, laissez simplement l'application de journalisation toujours enregistrer la ligne dans un fichier tout en la tronquant (c'est-à-dire> au lieu de >>)

Une certaine optimisation peut être obtenue en "devinant" cependant, il suffit d'ouvrir le fichier et avec la largeur moyenne de la ligne de journal, vous pouvez deviner où serait la dernière ligne. Aller à cette position avec fseek et trouver la dernière ligne.

0
Wolph

Voici une compilation des réponses ici enveloppées dans une fonction qui peut spécifier combien de lignes doivent être retournées.

function getLastLines($path, $totalLines) {
  $lines = array();

  $fp = fopen($path, 'r');
  fseek($fp, -1, SEEK_END);
  $pos = ftell($fp);
  $lastLine = "";

  // Loop backword until we have our lines or we reach the start
  while($pos > 0 && count($lines) < $totalLines) {

    $C = fgetc($fp);
    if($C == "\n") {
      // skip empty lines
      if(trim($lastLine) != "") {
        $lines[] = $lastLine;
      }
      $lastLine = '';
    } else {
      $lastLine = $C.$lastLine;
    }
    fseek($fp, $pos--);
  }

  $lines = array_reverse($lines);

  return $lines;
}
0
DynamicDan

Ceci est ma solution avec une seule boucle

        $line = '';
        $f = fopen($file_path, 'r');
        $cursor = 0 ;
        do  {
            fseek($f, $cursor--, SEEK_END);
            $char = fgetc($f);
            $line = $char.$line;
        } while (
                $cursor > -1 || (
                 ord($char) !== 10 &&
                 ord($char) !== 13
                )
        );
0
caiofior

Code non testé des commentaires de http://php.net/manual/en/function.fseek.php

jim at lfchosting dot com 05-Nov-2003 02:03
Here is a function that returns the last line of a file.  This should be quicker than reading the whole file till you get to the last line.  If you want to speed it up a bit, you can set the $pos = some number that is just greater than the line length.  The files I was dealing with were various lengths, so this worked for me. 

<?php 
function readlastline($file) 
{ 
        $fp = @fopen($file, "r"); 
        $pos = -1; 
        $t = " "; 
        while ($t != "\n") { 
              fseek($fp, $pos, SEEK_END); 
              $t = fgetc($fp); 
              $pos = $pos - 1; 
        } 
        $t = fgets($fp); 
        fclose($fp); 
        return $t; 
} 
?>
0
Syntax Error