2012-06-06 14 views
5

Tengo un archivo de texto de 1.3GB del que necesito extraer información en PHP. Lo he investigado y he propuesto algunas formas diferentes de hacer lo que tengo que hacer, pero como siempre, después de aclarar un poco qué método sería mejor o si existe otro mejor que yo no conozca.¿La mejor manera de extraer texto de un archivo de texto de 1.3GB usando PHP?

La información que necesito en el archivo de texto son solo los primeros 40 caracteres de cada línea, y hay alrededor de 17 millones de líneas en el archivo. Los 40 caracteres de cada línea se insertarán en una base de datos.

Los métodos que tengo están debajo;

// REMOVE TIME LIMIT 
set_time_limit(0); 
// REMOVE MEMORY LIMIT 
ini_set('memory_limit', '-1'); 
// OPEN FILE 
$handle = @fopen('C:\Users\Carl\Downloads\test.txt', 'r'); 
if($handle) { 
    while(($buffer = fgets($handle)) !== false) { 
     $insert[] = substr($buffer, 0, 40); 
    } 
    if(!feof($handle)) { 
     // END OF FILE 
    } 
    fclose($handle); 
} 

anterior es el siguiente cada línea a la vez y obtener los datos, he ordenado todas las inserciones de bases de datos, haciendo 50 inserciones en un momento más de diez veces en una transacción.

El siguiente método es el mismo que el anterior pero realmente llama al file() para almacenar todas las líneas en una matriz antes de hacer un foreach para obtener los datos? No estoy seguro acerca de este método, ya que la matriz tendría esencialmente más de 17 millones de valores.

Otro método sería extraer solo una parte del archivo, reescribir el archivo con los datos no utilizados, y después de que esa parte se haya ejecutado, recuperar el script mediante una llamada header.

¿Cuál sería la mejor manera en términos de hacer esto de la manera más rápida y eficiente? ¿O hay una mejor manera de abordar esto en lo que he pensado?

También planeo usar este script con wamp, pero ejecutarlo en un navegador mientras se prueba ha causado problemas con el tiempo de espera incluso con establecer el tiempo de script en 0. ¿Hay alguna forma de que pueda ejecutar el script sin acceder al página a través de un navegador?

+0

Para el último punto, 'php path/to/script.php' ejecutará la secuencia de comandos. – sarnold

+0

@sarnold ¿acabo de hacer eso desde la línea de comandos? Gracias – Griff

+1

Sí, directamente desde la línea de comando. También puede convertirlo en un archivo de script ejecutable si tiene la intención de ejecutarlo a menudo agregando '#!/Path/to/php' en la primera línea del script y luego ejecutando' chmod 755 path/to/script' o 'chmod 500' o los permisos apropiados que desee. – sarnold

Respuesta

5

Lo tiene bien hasta ahora, no use la función "file()" ya que probablemente golpearía el límite de uso de RAM y terminaría su script.

Ni siquiera acumularía cosas en la matriz "insert []", ya que eso también desperdiciará RAM. Si puede, inserte en la base de datos de inmediato.

Por cierto, hay una buena herramienta llamada "cortar" que puede utilizar para procesar el archivo.

cut -c1-40 file.txt 

Incluso podría redireccionar el código de corte estándar a un script PHP que se inserta en la base de datos.

cut -c1-40 file.txt | php -f inserter.php 

inserter.php podría leer líneas de php: // stdin e insertar en DB.

"cortar" es una herramienta estándar disponible en todos los Linux, si usa Windows puede obtenerlo con shell MinGW, o como parte de msystools (si usa git) o ​​instalar la aplicación win32 nativa usando gnuWin32.

+0

¿No sería eso demasiado para que mysql lo maneje? ¿Hacer una inserción 17 millones de veces o 50 a la vez? Después de insertar 50, la matriz se reinicia. – Griff

+0

@Griff, es el acceso de matriz de PHP frente a la velocidad de inserción de MySQL. Podría ser más rápido, pero también podría ser más lento. La única manera de verificar eso sería hacer un punto de referencia. Además, usar la instrucción INSERT preparada con solo cambiar los parámetros podría ayudar. –

+0

Gracias por esta información. Empezaré a trabajar de inmediato y te haré saber cómo me llevo :) – Griff

2

¿Por qué estás haciendo esto en PHP cuando tu RDBMS casi seguramente tiene funcionalidad de importación masiva integrada? MySQL, por ejemplo, tiene LOAD DATA INFILE:

LOAD DATA INFILE 'data.txt' 
INTO TABLE `some_table` 
    FIELDS TERMINATED BY '' 
    LINES TERMINATED BY '\n'; 
    (@line) 
SET `some_column` = LEFT(@line, 40); 

Una consulta.

MySQL también tiene la utilidad mysqlimport que envuelve esta funcionalidad desde la línea de comandos.

+0

Mi host compartido no me permite usar 'INFILE', era mi primera opción. – Griff

1

Ninguna de las anteriores. El problema con el uso de fgets() es que no funciona como esperaba. Cuando se alcanzan los caracteres máximos, la siguiente llamada al fgets() continuará en la misma línea. Ha identificado correctamente el problema con el uso de file(). El tercer método es una idea interesante, y también podría llevarlo a cabo con otras soluciones.

Dicho esto, su primera idea de usar fgets() es bastante cercana, sin embargo, tenemos que modificar ligeramente su comportamiento. He aquí una versión personalizada que funcionará como era de esperar:

function fgetl($fp, $len) { 
    $l = 0; 
    $buffer = ''; 
    while (false !== ($c = fgetc($fp)) && PHP_EOL !== $c) { 
     if ($l < $len) 
      $buffer .= $c; 
     ++$l; 
    } 
    if (0 === $l && false === $c) { 
     return false; 
    } 
    return $buffer; 
} 

ejecutar la operación de inserción inmediatamente o va a desperdiciar memoria. Asegúrese de estar usando prepared statements para insertar tantas filas; esto reducirá drásticamente el tiempo de ejecución. No desea enviar la consulta completa en cada inserción cuando solo puede enviar los datos.

+0

¿Sigue siendo un problema desde PHP 4.3.0? Además, ¿cómo será la velocidad con 76 veces más llamadas a funciones? – Wiseguy

+0

Creo que este es el comportamiento esperado de fgets(). La velocidad no debería ser un problema si está utilizando declaraciones preparadas (http://php.net/manual/en/pdo.prepared-statements.php) – siimsoni

+0

@KSiimson Estoy usando sentencias preparadas 'PDO', @Wiseguy esto es ¿Qué pensé que al omitir el atributo 'length' haré lo que quiero? – Griff

Cuestiones relacionadas