I'm writing a CSV file in PHP using fputcsv($file, $data)
. It all works, however I can't just open it in Excel but have to import it and specify the encoding and which delimiter to use (in a wizard). I've seen exports from other websites that open correctly just by clicking on them and now would like to know what I should do to my file to achieve that.
I tried using this library: http://code.google.com/p/parsecsv-for-php/ But I couldn't even get it to run and am not really confident if it would really help me...
This is how I make Excel readable CSV files from PHP :
For exemple :
$headers = array('Lastname :', 'Firstname :');
$rows = array(
array('Doe', 'John'),
array('Schlüter', 'Rudy'),
array('Alvarez', 'Niño')
);
// Create file and make it writable
$file = fopen('file.csv', 'w');
// Add BOM to fix UTF-8 in Excel
fputs($file, $bom = (chr(0xEF) . chr(0xBB) . chr(0xBF)));
// Headers
// Set ";" as delimiter
fputcsv($file, $headers, ";");
// Rows
// Set ";" as delimiter
foreach ($rows as $row) {
fputcsv($file, $row, ";");
}
// Close file
fclose($file);
// Send file to browser for download
$dest_file = 'file.csv';
$file_size = filesize($dest_file);
header("Content-Type: text/csv; charset=utf-8");
header("Content-disposition: attachment; filename=\"file.csv\"");
header("Content-Length: " . $file_size);
readfile($dest_file);
Works with Excel 2013.
this is really a mess. You surely can use the sep=; or sep=, or sep=\\t or whatever to make Excel aware of a separator used in your CSV. Just put this string at the beginning of your CSV contents. Eg:
fwrite($handle, "sep=,\n");
fputcsv($handle,$yourcsvcontent);
This works smoothly. BUT, it doesn't work in combination with a BOM which is required to make Excel aware of UTF-8 in case you need to support special characters or MB respectively.
In the end to make it bullet-proof you need to read out users locale and set the Separator accordingly, as mentioned above. Put a BOM ("\\xEF\\xBB\\xBF")
at the begining of your CSV content, then write the CSV like eg: fputcsv($handle, $fields, $user_locale_seperator);
where $user_locale_seperator
is the separtator you retrieved by checking the user's locale. Not comfortable but it works...
Despite the "C=comma" in CVS, Excel uses your locale native separator. So supposing fputcsv
always uses a comma, it won't work, if your locale separator is for example a semicolon.
What Google AdSense does, when you click "Export to Excel CSV" , is that it uses Tab as a separator. And that works.
To replicate that, set the third parameter ( delimiter
) of fputcsv
to override the default comma. Eg for Tab use: fputcsv($handle, $fields, "\\t");
Compare the format of the CSV that works for you against the one generated by fputcsv
.
Consider including example of both in your question. You might get better answers.
You may have an encoding issue.
Try this post:
http://onwebdev.blogspot.com.es/2010/10/php-encoding-of-csv-file-for-excel.html
I notice that you need to consider: Content-Type
header BOM (Byte Order Mark) Actual character encoding in the file
With BOM (works):
$bom = pack("CCC", 0xEF, 0xBB, 0xBF);
header('Content-Type: text/csv');
header('Content-Length: '.(strlen($csv)+strlen($bom)));
header('Content-Disposition: attachment;filename=my.csv');
echo $bom;
echo $csv;
Without BOM (works but you need to replace “smart quotes” then run utf8_decode
on each value or cell, and it converts some characters, for example FRĒ is converted to FRE')
header('Content-Type: application/csv;charset=utf-8');
header('Content-Length: '.strlen($csv));
header('Content-Disposition: attachment;filename=my.csv');
echo $csv;
If the wrong combination of charset and BOM are used, it just comes out wrong when opening in MS Excel.
Bonus fact: mb_strlen
tells you the number of characters, strlen
tells you the number of bytes. You do NOT want to use mb_strlen
for calculating the Content-Length header.
Bonus 2: replace microsoft "smart" characters (em dash, curly quotes, etc):
$map = array(chr(145) => "'"
,chr(146) => "'"
,chr(147) => '"'
,chr(148) => '"'
,chr(149) => '-'
,chr(150) => '-'
,chr(151) => '-'
,chr(152) => '-'
,chr(152) => '-'
,chr(171) => '-'
,chr(187) => '-'
);
// faster that strtr
return str_replace( array_keys($map), $map, $str );
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.