Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Fputcsv и коды новой строки

Автор вопроса: EmmySОтветов: 7

Я использую 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 за выделение проблемы, с которой я столкнулся Mark Baker 03.11.2010
  • Вы включаете заголовок поля csv? ajreal 16.11.2010
  • При сравнении приведенного выше кода с принятым решением (при условии, что этот файл создается в Linux) создается впечатление, что исходная проблема могла заключаться в пересылке файла клиенту, а не обязательно в его создании. Например, загрузка исходного файла (с CR+LF EOL) через FTP в режиме ASCII из Linux в Windows могла привести к повреждению концов строк. MrWhite 25.10.2013

phpnewlinefputcsv

Ответы

Ответ 1 · рейтинг 26 · lucaferrario

Это улучшенная версия отличного ответа @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;
}

Ответ 2 · рейтинг 6 · Bob Siefkes

Использование функции php fputcsv записывает только \n и не может быть настроено. Это делает функцию бесполезной для среды Microsoft, хотя некоторые пакеты также обнаруживают новую строку Linux.

Тем не менее преимущества fputcsv заставляли меня искать решение для замены символа новой строки непосредственно перед отправкой в ​​файл. Это можно сделать, сначала передав fputcsv в сборку во временном потоке php. Затем адаптируйте символы новой строки к тому, что вы хотите, а затем сохраните в файл. Нравится:

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);

Ответ 3 · рейтинг 4 · Torge

Как указал 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);
...

Ответ 4 · рейтинг 2 · Zak

в качестве альтернативы вы можете вывести в собственном формате unix (только \n), а затем запустить unix2dos для полученного файла, чтобы преобразовать его в \r\n в соответствующих местах. Просто будьте осторожны, чтобы ваши данные не содержали \n. Кроме того, я вижу, что вы используете разделитель по умолчанию ~ . попробуйте разделитель по умолчанию \t .

Ответ 5 · рейтинг 1 · PPC-Coder

Я имел дело с похожей ситуацией. Вот решение, которое я нашел, которое выводит CSV-файлы с окончаниями строк, удобными для Windows.

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

Я не смог использовать, так как я пытаюсь передать файл клиенту и не могу использовать fseeks.

Ответ 6 · рейтинг -1 · Zak

Windows нуждается в \r\n в качестве комбинации возврата linebreak/carriage для отображения отдельных строк.

Ответ 7 · рейтинг -3 · EmmyS

В конце концов я получил ответ на бирже экспертов; вот что сработало:

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

для использования вместо стандартного fputcsv.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы