Fputcsv и коды новой строки
Я использую fputcsv в PHP для вывода файла с разделителями-запятыми запроса базы данных. При открытии файла в gedit в Ubuntu он выглядит корректно - каждая запись имеет разрыв строки (нет видимых символов разрыва строки, но вы можете сказать, что каждая запись разделена, и открытие ее в электронной таблице OpenOffice позволяет мне правильно просматривать файл.)
Однако мы отправляем эти файлы клиенту в Windows, и в их системах файл поставляется в виде одной большой длинной строки. Открывая его в Excel, он не распознает несколько строк вообще.
Я читал здесь несколько вопросов, которые довольно похожи, в том числе этот, который включает ссылку на действительно информативный "Великая новая раскол" .
К сожалению, мы не можем просто сказать нашим клиентам, чтобы они открывали файлы в "умном" редакторе. Они должны иметь возможность открывать их в Excel. Есть ли какой-либо программный способ обеспечения добавления правильных символов новой строки, чтобы файл можно было открыть в программе для работы с электронными таблицами на любой ОС?
Я уже использую настраиваемую функцию для принудительного цитирования всех значений, поскольку fputcsv является выборочным. Я пробовал сделать что-то вроде этого:
function my_fputcsv($handle, $fieldsarray, $delimiter = "~", $enclosure ='"'){
$glue = $enclosure . $delimiter . $enclosure;
return fwrite($handle, $enclosure . implode($glue,$fieldsarray) . $enclosure."\r\n");
}
Но когда файл открывается в текстовом редакторе Windows, он все равно отображается как одна длинная строка.
Ответы
Ответ 1
В конце концов я получил ответ на обмене экспертами; вот что сработало:
function my_fputcsv($handle, $fieldsarray, $delimiter = "~", $enclosure ='"'){
$glue = $enclosure . $delimiter . $enclosure;
return fwrite($handle, $enclosure . implode($glue,$fieldsarray) . $enclosure.PHP_EOL);
}
вместо стандартного fputcsv.
Ответ 2
// 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);
}
}
Ответ 3
Это улучшенная версия @John Douthat отличный ответ, сохраняющий возможность использования пользовательских разделителей и корпусов и возвращающий исходный вывод 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;
}
Ответ 4
Использование php-функции fputcsv записывает только \n и не может быть настроено. Это делает функцию бесполезной для среды microsoft, хотя некоторые пакеты также обнаружат новую линию linux.
По-прежнему преимущества fputcsv заставляли меня копаться в решении для замены символа новой строки перед отправкой в файл. Это может быть сделано путем потоковой передачи fputcsv в сборку в потоке php temp. Затем адаптируйте символ (-ы) новой строки так, как хотите, а затем сохраните файл. Вот так:
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);
Ответ 5
в качестве альтернативы вы можете выводить в собственном унифицированном формате (только\n), а затем запускать unix2dos в результирующем файле для преобразования в \r\n в соответствующих местах. Просто будьте осторожны, чтобы ваши данные не содержали \n. Кроме того, я вижу, что вы используете разделитель по умолчанию ~. попробуйте разделитель по умолчанию из\t.
Ответ 6
Я имел дело с подобной ситуацией. Вот решение, которое я нашел, выводит CSV файлы с дружественными окнами окончаниями.
http://www.php.net/manual/en/function.fputcsv.php#90883
Мне не удалось использовать, так как я пытаюсь передать файл клиенту и не могу использовать fseeks.
Ответ 7
Как webbiedave указал (спасибо!), возможно, самый чистый способ - использовать фильтр потока.
Это немного сложнее, чем другие решения, но даже работает с потоками, которые не редактируются после их написания (например, с помощью $handle = fopen('php://output', 'w');
)
Вот мой подход:
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);
...
Ответ 8
Windows нуждается в \r\n
в качестве linebreak/каретки, чтобы показать отдельные строки.