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, он по-прежнему отображается как одна длинная строка.


person EmmyS    schedule 02.11.2010    source источник
comment
+1 за выделение проблемы, с которой я столкнулся   -  person Mark Baker    schedule 03.11.2010
comment
Вы включаете заголовок поля csv?   -  person ajreal    schedule 16.11.2010
comment
При сравнении приведенного выше кода с принятым решением (при условии, что этот файл создается в Linux) создается впечатление, что исходная проблема могла заключаться в пересылке файла клиенту, а не обязательно в его создании. Например, загрузка исходного файла (с CR+LF EOL) через FTP в режиме ASCII из Linux в Windows могла привести к повреждению концов строк.   -  person MrWhite    schedule 25.10.2013


Ответы (8)


Это улучшенная версия отличного ответа @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;
}
person lucaferrario    schedule 23.01.2014
comment
Что здесь $handle? - person Miomir Dancevic; 25.06.2020
comment
@MiomirDancevic это ресурс, созданный с помощью функции fopen(). - person lucaferrario; 07.07.2020

Использование функции 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);
person Bob Siefkes    schedule 11.03.2011
comment
Спасибо, что поделились этим магазином! - person jjwdesign; 03.11.2011

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

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

person Zak    schedule 02.11.2010
comment
Можно ли установить unix2dos на сервер и вызывать его программно? Потому что мне буквально приходится вручную связываться с этими файлами - они создаются программно и программно отправляются клиенту по электронной почте. - person EmmyS; 02.11.2010
comment
в php вы можете вызывать программы из языка: - person Zak; 03.11.2010
comment
Спасибо. К сожалению, мне только что сказали, что этот сайт будет размещен в облаке, поэтому у меня нет возможности узнать, установлен он или нет, и я не могу контролировать его установку, если это не так. Мне нужен способ сделать это только стандартными командами php. - person EmmyS; 03.11.2010

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

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

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

person PPC-Coder    schedule 10.11.2010
comment
Спасибо! Я попробую и посмотрю, поможет ли это. - person EmmyS; 10.11.2010

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

person Zak    schedule 02.11.2010
comment
Да, я знаю об этом, и если вы посмотрите на код, который я разместил, вы увидите, что я добавляю \r\n к строке, которую я пишу в файл. Вроде не имеет значения - винда так не читает. - person EmmyS; 02.11.2010

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

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

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

person EmmyS    schedule 16.11.2010
comment
Я знаю, что это старый ответ, но было бы упущением, если бы я не упомянул, что это ужасный ответ от экспертного обмена. Он даже не обрабатывает экранирование двойными кавычками. Вместо этого используйте потоковой фильтр. Вот почему они там. php.net/manual/en/function.stream-filter- регистр.php - person webbiedave; 01.08.2011

person    schedule
comment
блестяще! короче и лучше, чем любое другое решение вокруг - person max4ever; 01.02.2012
comment
Отличный ответ! Я разместил улучшенную версию ниже, сохраняя возможность использования пользовательских разделителей и корпусов и возвращая исходный вывод fputcsv. - person lucaferrario; 23.01.2014