Leia a última linha do arquivo

Estive com um problema. Tenho um registro em uma checkbox do Linux em que está escrito o resultado de vários processos em execução. Este arquivo pode ser realmente grande às vezes e eu preciso ler a última linha desse arquivo.

O problema é que esta ação será chamada através de uma solicitação AJAX com bastante frequência e quando o tamanho do arquivo desse log tiver mais de 5-6MB, não é bom para o servidor. Então, eu estou pensando que eu tenho que ler a última linha, mas não para ler todo o arquivo e passar por ele ou carregá-lo na RAM, pois isso acabaria por carregar a minha checkbox.

Existe alguma otimização para esta operação para que ele funcione suave e não prejudique o servidor ou mate o Apache?

Outra opção que eu tenho é exec('tail -n 1 /path/to/log') mas isso não soa tão bom.

Mais tarde, edite: NÃO QUERO colocar o arquivo na RAM porque pode ficar enorme. fopen() não é uma opção.

Isso deve funcionar:

 $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; 

Use fseek . Você procura a última posição e procura-a para trás (use ftell para contar a posição atual) até encontrar “\ 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--); } 

NOTA: Não testei. Você pode precisar de algum ajuste.

UPDATE: Obrigado Syntax Error para apontar sobre o arquivo vazio.

: D

UPDATE2: corrigiu outro erro de syntax, ponto e vírgula faltando em $LastLine = ""

Você está procurando a function fseek . Existem exemplos úteis de como ler a última linha de um arquivo na seção de comentários lá.

Se você conhece o limite superior do comprimento da linha, você poderia fazer algo assim.

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

Em resposta à edição : fopen apenas adquire um identificador ao arquivo (ou seja, certifique-se de que ele existe, o processo tem permissão, permite que um processo esteja usando o arquivo, etc …). Neste exemplo, somente 1024 caracteres do arquivo serão lidos na memory.

 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; } 

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

Este é o código de Ionuţ G. Stan

Eu modifiquei seu código um pouco e tornei uma function para reutilização

 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’);

você conseguirá a última linha

Seu problema é semelhante a este

A melhor abordagem para evitar carregar todo o arquivo na memory parece ser:

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

Seria possível otimizar isso do outro lado? Se assim for, apenas deixe o aplicativo de registro sempre registrar a linha em um arquivo enquanto o trunca (ou seja, em vez de >>)

Alguma otimização pode ser conseguida por “adivinhar”, basta abrir o arquivo e, com a largura média da linha de log, você poderia adivinhar onde a última linha seria. Ir para essa posição com fseek e encontrar a última linha.

Código não testado dos comentários 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.  

Esta é a minha solução com apenas um loop

  $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 ) ); 

Aqui está uma compilation das respostas aqui envolvidas em uma function que pode especificar quantas linhas devem ser retornadas.

 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; } 

Eu usaria o arquivo () que lê o arquivo em uma matriz, inverter a matriz e obter o primeiro elemento ou exibir a matriz:

$ last_line = array_pop (arquivo ($ filename));

Se você deseja que o desempenho tente abrir o arquivo e usando o ponteiro do arquivo para navegar para ele.