2010-07-03 19 views
10

Estoy leyendo un archivo que contiene alrededor de 50k líneas usando la función file() en Php. Sin embargo, da un error de falta de memoria ya que los contenidos del archivo se almacenan en la memoria como una matriz. ¿Hay alguna otra manera?Manera con menos memoria para leer un archivo en PHP

Además, las longitudes de las líneas almacenadas son variables.

Aquí está el código. Además, el archivo es 700kB, no mB.

private static function readScoreFile($scoreFile) 
{ 
    $file = file($scoreFile); 
    $relations = array(); 

    for($i = 1; $i < count($file); $i++) 
    { 
     $relation = explode("\t",trim($file[$i])); 
     $relation = array(
         'pwId_1' => $relation[0], 
         'pwId_2' => $relation[1], 
         'score' => $relation[2], 
         ); 
     if($relation['score'] > 0) 
     { 
      $relations[] = $relation; 
     } 
    } 

    unset($file); 
    return $relations; 
} 
+0

Sé que esta pregunta es viejo, pero dos cosas aquí. 1. lea el archivo línea por línea. 2. El error de falta de memoria podría ser que también está almacenando todo en una matriz, por lo general no es una buena idea sin algún tipo de control y conocimiento de la memoria que tiene – Atherion

Respuesta

13

Uso fopen, fread y fclose para leer un archivo secuencialmente:

$handle = fopen($filename, 'r'); 
if ($handle) { 
    while (!feof($handle)) { 
     echo fread($handle, 8192); 
    } 
    fclose($handle); 
} 
+0

Esto no funciona, quiero leer línea por línea.Sus mutliple que regresan líneas en cada fread (supongo que 8192 bytes) – Chetan

+7

reemplazar fread con "fgets": fgets - Obtiene una línea del fichero apuntado –

+0

Puede utilizar una línea variable $ intermedia para almacenar los bytes de cada línea, y luego se hacen eco de $ line . Fread es probablemente una de las maneras más eficientes de transmitir el archivo, así que lea los resultados de fread (y anexe a $ line) hasta que encuentre un salto de línea. Luego haga lo que quiera con esa línea, luego configure $ line = "", y continúe agregando los resultados de fread a $ line. – luiscubal

9

EDITAR después de la actualización de la pregunta y comments to answer of fabjoa:

Definitivamente hay algo raro si un archivo 700kb come hasta 140MB de memoria con ese código que diste (sin embargo, podrías tener unset $ relación al final de cada iteración). Considere usar un depurador para recorrerlo y ver qué sucede. También puede ser que desee considerar volver a escribir el código para utilizar SplFileObject's CSV functions así (or their procedural cousins)

SplFileObject::setCsvControl example

$file = new SplFileObject("data.csv"); 
$file->setFlags(SplFileObject::READ_CSV); 
$file->setCsvControl('|'); 
foreach ($file as $row) { 
    list ($fruit, $quantity) = $row; 
    // Do something with values 
} 

Para una aproximación orientada a objetos para iterar sobre el archivo, intente SplFileObject:

SplFileObject::fgets example

$file = new SplFileObject("file.txt"); 
while (!$file->eof()) { 
    echo $file->fgets(); 
} 

SplFileObject::next example

// Read through file line by line 
$file = new SplFileObject("misc.txt"); 
while (!$file->eof()) { 
    echo $file->current(); 
    $file->next(); 
} 

o incluso

foreach(new SplFileObject("misc.txt") as $line) { 
    echo $line; 
} 

relacionados (si no duplicar) Más o menos:

+0

Creo que esto todavía puede utilizar una gran cantidad de memoria, ya que creo que continúa leyendo hasta que encuentra un final de línea. – Artefacto

+0

mismo que el anterior, quiero leer línea por línea (terminado por \ n) – Chetan

+0

@Artefacto así, todavía puede usar 'SplFileObject :: setMaxLineLen' si eso es un problema. – Gordon

0

asignar más memoria durante la operación, tal vez algo como en i_set ('memory_limit', '16M') ;. No olvide volver a la asignación de memoria inicial una vez que finalice la operación

+0

Estoy bastante seguro de que no tiene que restablecer el límite de memoria después de la operación, solo se aplica al script actualmente en ejecución. –

+0

Ya estoy usando 140MB de memoria (hay muchas cosas que van de la lectura del archivo) – Chetan

+1

@Chetan esto me suena sospechoso. 50k líneas no son mucho. La [Biblia King James] (http://www.gutenberg.org/etext/26361) tiene alrededor de 20k líneas, tiene 1MB en texto plano y solo ocupa alrededor de ~ 3MB cuando se lee con el archivo(). ¿Cuál es el tamaño total en bytes de su archivo? – Gordon

1

Si no conoce la longitud máxima de la línea y no se siente cómodo utilizando un número mágico para la longitud máxima de la línea, necesitará haga un escaneo inicial del archivo y determine la longitud de línea máxima.

Aparte de que el siguiente código serle de ayuda:

// length is a large number or calculated from an initial file scan 
    while (!feof($handle)) { 
     $buffer = fgets($handle, $length); 
     echo $buffer; 
    } 
Cuestiones relacionadas