2012-05-24 11 views
6

Estoy usando PHP para importar datos desde un archivo CSV utilizando fgetcsv(), que produce una matriz para cada fila. Inicialmente, tuve el conjunto límite de caracteres en 1024, así:Asegúrese de que fgetcsv() lea toda la línea

while ($data = fgetcsv($fp, 1024)) { 
    // do stuff with the row 
} 

Sin embargo, CSV con más de 200 columnas superaron el límite de 1024 en muchas filas. Esto causó que la lectura de la línea se detuviera en medio de una fila, y luego la siguiente llamada a fgetcsv() comenzaría donde la anterior dejó, y así sucesivamente hasta que se alcanzó un EOL.

Desde entonces, he aumentado este límite a 4096, lo que debería ocuparme de la mayoría de los casos, pero me gustaría verificar que se haya leído toda la línea después de cada línea. ¿Cómo hago esto?

Estaba pensando en verificar el final del último elemento de la matriz para los caracteres de fin de línea (\ n, \ r, \ r \ n), pero no serían analizados por la llamada fgetcsv() ?

+0

Además, me doy cuenta de que podría determinar mediante programación la línea más larga del archivo, pero esto podría ser una gran sobrecarga en archivos CSV realmente grandes. Me gustaría averiguar cómo asegurar que cada línea se lea en su totalidad sobre la marcha. –

Respuesta

1

Gracias por las sugerencias, pero estas soluciones realmente no resolvieron el problema de saber que representamos la línea más larga sin dejar de ofrecer un límite. Pude lograr esto usando el comando wc -L de UNIX a través de shell_exec() para determinar la línea más larga en el archivo antes de comenzar la obtención de la línea. El código es el siguiente:

// open the CSV file to read lines 
$fp = fopen($sListFullPath, 'r'); 

// use wc to figure out the longest line in the file 
$longestArray = explode(" ", shell_exec('wc -L ' . $sListFullPath)); 
$longest_line = (int)$longestArray[0] + 4; // add a little padding for EOL chars 

// check against a user-defined maximum length 
if ($longest_line > $line_length_max) { 
    // alert user that the length of at least one line in the CSV is too long 
} 

// read in the data 
while ($data = fgetcsv($fp, $longest_line)) { 
    // do stuff with the row 
} 

Este enfoque garantiza que todas las líneas se lee en su totalidad y todavía proporciona una red de seguridad para las líneas muy largas sin pisar a través de todo el archivo PHP con la línea por línea.

6

Simplemente omita el parámetro de longitud. Es opcional en PHP5.

while ($data = fgetcsv($fp)) { 
    // do stuff with the row 
} 
3

Simplemente no especifique un límite, y fgetcsv() sorberá tanto como sea necesario para capturar una línea completa. Si especifica un límite, depende enteramente de USTED escanear el flujo de archivos y asegurarse de no cortar algo en el medio.

Sin embargo, tenga en cuenta que no especificar un límite puede ser riesgoso si no tiene control sobre la generación de este .csv en primer lugar. Sería fácil saturar su servidor con un CSV malicioso que tiene muchos terabytes de datos en una sola línea.

+0

Consideré esto, pero 2 cosas: 1) NO tengo control sobre la generación de CSV. Son proporcionados por clientes (no confiables), así que realmente quiero imponer algún tipo de límite. 2) El manual dice "Omitiendo este parámetro (o configurándolo a 0 en PHP 5.0.4 y posterior), la longitud máxima de la línea no está limitada, lo cual es un poco más lento". Tengo miedo de lo que se agregará "un poco más lento" con un archivo CSV que tiene 100k + filas. –

+2

ligeramente más lento = lee el archivo en trozos hasta que encuentra un salto de línea en algún lugar de ese trozo, luego rebobina el puntero del archivo para que la siguiente lectura aparezca inmediatamente DESPUÉS del descanso. –

+1

Usted PODRÍA hacer su propia lectura línea por línea por separado, luego use [str_get_csv()] (http://php.net/manual/en/function.str-getcsv.php) para realizar el análisis csv-> array . –

0

Tendría cuidado con su solución final. Pude cargar un archivo llamado /.;ls -a;.csv para realizar la inyección de comandos. Asegúrese de validar la ruta del archivo si usa este enfoque. Además, podría ser una buena idea proporcionar un default_length en el caso de que su wc falle por algún motivo.

// use wc to find max line length 
// uses a hardcoded default if wc fails 
// this is relatively safe from command 
// injection since the file path is a tmp file 
$wc = explode(" ", shell_exec('wc -L ' . $validated_file_path)); 
$longest_line = (int)$wc[0]; 
$length = ($longest_line) ? $longest_line + 4 : $default_length; 
Cuestiones relacionadas