10

I create a CSV file for download by our client using

$output = fopen('php://output', 'w');

and using fputcsv() to write data to a CSV file which is downloaded by the client.

I am running PHP on Linux and consequently the line endings are not interpreted by many Windows applications.

I could write the CSV file to a directory on the server, read it back in and perform a str_replace() from \n to \r\n, but this seems a rather clunky way of solving the problem. Is there a way to perform the conversion without creating a physical file?

Kalessin
  • 2,282
  • 2
  • 22
  • 24
  • Unless the user is opening the file in a Windows text editor that requires CRLF, or some pretty old Windows apps, then it shouldn't actually matter... most Windows applications these days do recognise a simple LF instead – Mark Baker Oct 04 '12 at 08:11
  • 1
    Sorry for +1 you were at 1337, the most graceful rep. But your question interests me, no way to change escape char too... – Alain Tiemblo Oct 04 '12 at 08:13
  • @MarkBaker: Yes, that's what I expected. Unfortunately, they're importing the CSV into some MIS they use, which doesn't appear to be any smarter than Notepad in this regard. – Kalessin Oct 04 '12 at 08:14
  • 1
    Have you read the comments in the manual for fputcsv? http://lv.php.net/manual/en/function.fputcsv.php#90883 – lix Oct 04 '12 at 08:15
  • @lix: Yes, I certainly have, and I could fall back on the specific function you linked to, but I wanted to use `fputcsv()` and just convert the line endings afterwards, rather than replicating the entire functionality of `fputcsv()` and adding the line ending conversion. The PHP user notes aren't always the best resource. That's why I come here :) – Kalessin Oct 04 '12 at 08:27
  • You can find a brilliant and short solutions here: [Setting Custom Line Endings in fputcsv()](http://stackoverflow.com/a/7961173/2931427) – Shoaib Shakeel Nov 01 '13 at 05:58

6 Answers6

24

You could use stream filters to accomplish this. This example writes to a physical file, but it should work fine for php://output as well.

// filter class that applies CRLF line endings
class crlf_filter extends php_user_filter
{
    function filter($in, $out, &$consumed, $closing)
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            // make sure the line endings aren't already CRLF
            $bucket->data = preg_replace("/(?<!\r)\n/", "\r\n", $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}
// register the filter
stream_filter_register('crlf', 'crlf_filter');

$f = fopen('test.csv', 'wt');
// attach filter to output file
stream_filter_append($f, 'crlf');
// start writing
fputcsv($f, array('1 1', '2 2'));
fclose($f);
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
2

Not sure if you can do this with PHP itself. There may be a way to change PHP's EOL for file writing, but it's probably system dependent. You don't have a windows system you could ping, do you? ;)

As for a real solution, instead of str_replace line-by-line, you could use the Linux program unix2dos (inverse of dos2unix) assuming you have it installed:

fputcsv($fh ...)
exec("unix2dos " . escapeshellarg($filename));
Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
1
  1. Create the file with \n line endings on a Linux machine
  2. FTP the file as ASCII from the Linux machine to a Windows machine
  3. Hey presto! All line endings are now \r\n in the file on the Windows machine
Wandering Zombie
  • 1,101
  • 13
  • 14
1

Rather than writing the file, a better solution is to use output buffering

function output($buffer) {
    return str_replace("\n", "\r\n", $buffer);
} 

ob_start('output');
fputcsv(....);
MightyPork
  • 18,270
  • 10
  • 79
  • 133
Tom B
  • 2,735
  • 2
  • 24
  • 30
1

Since PHP 8.1 fputcsv accepts a new $eol parameter to do this. By default is "\n" but can change it to "\r\n".

fputcsv($stream, $fields, eol: "\r\n");
Forfarle
  • 46
  • 3
-1

If PHP is not properly recognizing the line endings when reading files either on or created by a Macintosh computer, enabling the auto_detect_line_endings run-time configuration option may help resolve the problem. ini_set("auto_detect_line_endings", true);