2010-07-13 61 views
12

¿Cómo se lee un archivo revés línea por línea usando fseek?Leer un archivo línea por línea hacia atrás usando fseek

código puede ser útil. debe ser multiplataforma y php puro

muchas gracias de antemano

que respecta a

Jera

+0

¿Hay alguna razón por la que se debe buscar en lugar de las siguientes sugerencias que implican leer todo el archivo? ¿Qué tipo de datos estás leyendo? – salathe

+0

Si desea más ** solución compatible con la memoria **, esta es una respuesta mía que responde una pregunta similar: http://stackoverflow.com/questions/20561480/read-file-lines-backwards-fgets-with-php/ 26595154 # 26595154 –

Respuesta

14

La pregunta es preguntar usando fseek, por lo que solo podemos suponer que el rendimiento es un problema y file() no es la solución. Aquí es un enfoque simple usando fseek:

Mi archivo.txt

#file.txt 
Line 1 
Line 2 
Line 3 
Line 4 
Line 5 

Y el código:

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

Salida:

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

Usted no tiene que anexar a la matriz de $ lines como soy, puede imprimir la salida de inmediato si ese es el propósito de su secuencia de comandos. También es fácil introducir un contador si desea limitar el número de líneas.

$linesToShow = 3; 
$counter = 0; 
while ($counter <= $linesToShow && -1 !== fseek($fp, $pos, SEEK_END)) { 
    // Rest of code from example. After $lines[] = $currentLine; add: 
    $counter++; 
} 
+0

Después de rechazar mi edición ...: Debe verificar si el manejador de archivos es válido después de fopen() y luego libre con fclose() para liberarlo y evitar pérdidas de memoria. – StanE

+0

PHP_EOL depende de la plataforma y en algunos casos será insuficiente. Reemplazar la condición con ... ("\ n" == $ char) ... y omitir las líneas vacías lo hará más confiable – ANTARA

6

No se puede fseek línea por línea, ya que no se sabe cuánto tiempo las líneas son hasta que las lee.

Debe leer todo el archivo en una lista de líneas, o si el archivo es demasiado grande para eso y solo necesita las últimas líneas, leer fragmentos de tamaño fijo desde el final del archivo e implementar un poco más lógica complicada que detecta líneas de tales datos.

21

Si va a leer el archivo completo de todos modos, simplemente use file() para leer el archivo como una matriz (cada línea es cada elemento de la matriz) y luego use array_reverse() para voltear la matriz hacia atrás y recorrerla . O simplemente haz un reverse para loop donde comiences al final y decrementa en cada loop.

$file = file("test.txt"); 
$file = array_reverse($file); 
foreach($file as $f){ 
    echo $f."<br />"; 
} 
+1

O para evitar revertir la matriz, puede simplemente hacer un bucle desde $ i = count ($ file) - 1; $ i> 0; $ i ++) y lee $ file [$ i] –

+0

@EqSum yep, puede hacer eso. La parte media del bucle for debería ser '$ i> = 0' sin embargo. –

+0

¡Oye! Si está analizando un archivo realmente grande (millones de líneas), esto consumirá rápidamente mucha memoria al cargar todo en la memoria. ¡Esto no es una buena idea! –

8

Para revertir completamente un archivo:

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

Por supuesto, usted quiere línea por línea de reversión ...

 

$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") { 
     // analyse completed line $output[$ln] if need be 
     $ln++; 
     continue; 
     } 
    $output[$ln] = $char . ((array_key_exists($ln, $output)) ? $output[$ln] : ''); 
    } 
fclose($fl); 
print_r($output); 

Realmente sin embargo, Jonathan Kuhn tiene la mejor respuesta En mi humilde opinión arriba. Los únicos casos que no usaría su respuesta, que yo sepa si es file o como funciones no están disponibles a través de php.ini, sin embargo, el administrador se olvidó de fseek, o al abrir un archivo enorme acaba de obtener las últimas líneas de contenidos mágicamente guardar la memoria de esta manera.

Nota: El manejo de errores no incluido. Y, PHP_EOL no cooperó, así que usé "\ n" para denotar el final de la línea. Por lo tanto, lo anterior puede no funcionar en todos los casos.

+0

El código de la segunda parte, que analiza línea por línea, no funciona. –

2

Leyendo el archivo entero en una matriz y de marcha atrás está bien a menos que el archivo es enorme.

Usted puede realizar una lectura tamponada de su archivo de atrás hacia adelante con algo como esto:

  • establecer una buffer_size B - esto debe ser más largo de la línea prevista más larga de lo contrario se va a necesitar algo de lógica crece el tamaño del búfer cuando las líneas son demasiado largas
  • conjunto de corrección de longitud = archivo - bUFFER_SIZE
  • mientras que el offset> = 0
    • leer bUFFER_SIZE bytes desde el desplazamiento
    • leer una línea - que será incompleta, ya habremos saltado en el medio de una línea, por lo que queremos garantizar el buffer siguiente leemos extremos con él.offset = desplazamiento conjunto - buffer_size + longitud línea
    • descartar esa línea, lea todas las siguientes líneas en una matriz y revertirlas
    • proceso de esta matriz para hacer lo que quería hacer
13
<?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"; 
0

Este código lee el archivo al revés. Este código ignora las modificaciones en la lectura, ejemplo apache access.log nuevas líneas en procressing.

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