códigos fputcsv y newline

Estoy usando fputcsv en PHP para generar un archivo delimitado por comas de una consulta de base de datos. Al abrir el archivo en gedit en Ubuntu, parece correcto: cada registro tiene un salto de línea (sin caracteres de salto de línea visibles, pero se puede decir que cada registro está separado, y abrirlo en la hoja de cálculo de OpenOffice me permite ver el archivo correctamente).

Sin embargo, estamos enviando estos archivos a un cliente en Windows, y en sus sistemas, el archivo viene como una línea grande y larga. Al abrirlo en Excel, no reconoce varias líneas en absoluto.

He leído varias preguntas aquí que son bastante similares, incluida esta , que incluye un enlace a la explicación realmente informativa de Great Newline Schism .

Desafortunadamente, no podemos simplemente decirle a nuestros clientes que abran los archivos en un editor “más inteligente”. Deben poder abrirlos en Excel. ¿Existe alguna forma programática para garantizar que se agreguen los caracteres correctos de la línea nueva para que el archivo pueda abrirse en un progtwig de hoja de cálculo en cualquier sistema operativo?

Ya estoy usando una función personalizada para forzar las comillas alrededor de todos los valores, ya que fputcsv es selectivo al respecto. He intentado hacer algo como esto:

function my_fputcsv($handle, $fieldsarray, $delimiter = "~", $enclosure ='"'){ $glue = $enclosure . $delimiter . $enclosure; return fwrite($handle, $enclosure . implode($glue,$fieldsarray) . $enclosure."\r\n"); } 

Pero cuando el archivo se abre en un editor de texto de Windows, aún aparece como una sola línea larga.

 // Writes an array to an open CSV file with a custom end of line. // // $fp: a seekable file pointer. Most file pointers are seekable, // but some are not. example: fopen('php://output', 'w') is not seekable. // $eol: probably one of "\r\n", "\n", or for super old macs: "\r" function fputcsv_eol($fp, $array, $eol) { fputcsv($fp, $array); if("\n" != $eol && 0 === fseek($fp, -1, SEEK_CUR)) { fwrite($fp, $eol); } } 

Esta es una versión mejorada de la excelente respuesta de @John Douthat, que preserva la posibilidad de utilizar delimitadores y gabinetes personalizados y devolver el resultado original de fputcsv:

 function fputcsv_eol($handle, $array, $delimiter = ',', $enclosure = '"', $eol = "\n") { $return = fputcsv($handle, $array, $delimiter, $enclosure); if($return !== FALSE && "\n" != $eol && 0 === fseek($handle, -1, SEEK_CUR)) { fwrite($handle, $eol); } return $return; } 

Usando la función php, fputcsv solo escribe \ n y no se puede personalizar. Esto hace que la función no tenga valor para el entorno de Microsoft, aunque algunos paquetes también detectarán la línea nueva de Linux.

Aún así, los beneficios de fputcsv me mantuvieron buscando una solución para reemplazar el carácter de nueva línea justo antes de enviarlo al archivo. Esto se puede hacer mediante la transmisión de fputcsv a la comstackción en php temp stream primero. Luego, adapte los caracteres de nueva línea a lo que desee y luego guárdelos en el archivo. Me gusta esto:

 function getcsvline($list, $seperator, $enclosure, $newline = "" ){ $fp = fopen('php://temp', 'r+'); fputcsv($fp, $list, $seperator, $enclosure ); rewind($fp); $line = fgets($fp); if( $newline and $newline != "\n" ) { if( $line[strlen($line)-2] != "\r" and $line[strlen($line)-1] == "\n") { $line = substr_replace($line,"",-1) . $newline; } else { // return the line as is (literal string) //die( 'original csv line is already \r\n style' ); } } return $line; } /* to call the function with the array $row and save to file with filehandle $fp */ $line = getcsvline( $row, ",", "\"", "\r\n" ); fwrite( $fp, $line); 

alternativamente, puede generar resultados en formato nativo de Unix (solo \ n) y luego ejecutar unix2dos en el archivo resultante para convertirlo a \ r \ n en los lugares apropiados. Solo tenga cuidado de que sus datos no contengan \ n’s. Además, veo que estás usando un separador predeterminado de ~. intente con un separador predeterminado de \ t.

He estado lidiando con una situación similar. Aquí hay una solución que he encontrado que genera archivos CSV con terminaciones de línea amigables para Windows.

http://www.php.net/manual/en/function.fputcsv.php#90883

No pude usar el archivo porque bash transmitir un archivo al cliente y no puedo usar los fseeks.

Como webbiedave señaló (¡gracias!) Probablemente la forma más limpia es usar un filtro de flujo.

Es un poco más complejo que otras soluciones, pero incluso funciona en flujos que no son editables después de escribirles (como una descarga usando $handle = fopen('php://output', 'w'); )

Aquí está mi enfoque:

 class StreamFilterNewlines extends php_user_filter { function filter($in, $out, &$consumed, $closing) { while ( $bucket = stream_bucket_make_writeable($in) ) { $bucket->data = preg_replace('/([^\r])\n/', "$1\r\n", $bucket->data); $consumed += $bucket->datalen; stream_bucket_append($out, $bucket); } return PSFS_PASS_ON; } } stream_filter_register("newlines", "StreamFilterNewlines"); stream_filter_append($handle, "newlines"); fputcsv($handle, $list, $seperator, $enclosure); ... 

windows necesita \r\n como el combo linebreak / carriage return para mostrar líneas separadas.

Eventualmente obtuve una respuesta en intercambio de expertos; esto es lo que funcionó:

 function my_fputcsv($handle, $fieldsarray, $delimiter = "~", $enclosure ='"'){ $glue = $enclosure . $delimiter . $enclosure; return fwrite($handle, $enclosure . implode($glue,$fieldsarray) . $enclosure.PHP_EOL); } 

para ser utilizado en lugar de fputcsv estándar.