I've been trying, for some time now, to export a properly encoded and formated CSV file with PHP. But it's just not working. I've tried every tip in every CSV/PHP related thread on SOF, I've checked that the data in my database is UTF-8, it is. I've tried stuff like utf8_encode() on the whole CSV-line, I've checked that the actual PHP file is encoded in UTF-8, but still no success. When I run the file on http://csvlint.io/ I just get:
Your CSV appears to be encoded in ASCII-8BIT. We recommend you use UTF-8.
But I can't find a trace of any other encoding than UTF-8 anywhere in my code..
Basically this is my code:
First, I put all my CSV-rows in an array, then do this:
if (count($array) == 0)
{
return NULL;
}
ob_start();
$df = fopen("php://output", 'w');
$csv = utf8_encode("header1|header2|header3|header4|header5|header6|header7\r\n");
foreach($array as $line) {
$csv .= $line . "\r\n";
}
setlocale(LC_ALL, 'sv_SE', "swedish");
fwrite($df, "\xEF\xBB\xBF".$csv);
fclose($df);
return ob_get_clean();
And these are the headers sent:
$now = gmdate("D, d M Y H:i:s");
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: {$now} GMT");
header("Content-Encoding: UTF-8");
header("Content-Type: text/csv; charset=UTF-8");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
Any ideas?
The issue is the byte-order mark you're prepending to the output in this line:
fwrite($df, "\xEF\xBB\xBF".$csv);
If you change this to simply
fwrite($df, $csv);
You should find the resulting file validates just fine (or at least, the validator doesn't complain about its encoding).
Arguably this is a problem with the validator, since as the Wikipedia article notes,
The Unicode Standard permits the BOM in UTF-8, but does not require or recommend its use.
I don't recommend you use it either, as most software seems not to recognize byte-order marks. But if you must or you simply prefer to, you can safely ignore the warning from CSVLint.
Since that is apparently not the issue, the next thing I'd look at is whether or not the data is being retrieved from the database in UTF-8. (I'll take your word you've already checked carefully to make sure the data is being stored in UTF-8.) If you're using MySQL, this will depend on the configuration of the database server and any options you may be sending the database after connection.
The PHP manual has a section on character sets and MySQL, and there is also this helpful article about using PHP and MySQL together with UTF-8 data. If you're using a different database system, it likely has equivalent configuration options that should be checked.
The only other suggestions I can make are that you
Move the call to setlocale higher in the script, before string concatenation begins in the foreach loop. (I don't think this setting affects simple concatenation, but I'm not certain.)
Remove the Content-Encoding header from your output, as it is invalid the way it is currently being used.
Try to use this code:
$filename = 'csv/'.date('Y-m-d_H:i:s').'.csv';
$fp = fopen($filename, 'w');
foreach ($csvData as $fields) {
fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
fputcsv($fp, $fields, $delimiter = ';');
}
fclose($fp);
Related
So I'm trying to export a csv using PHP in which the contents contains UTF-8 character and I want the resultant csv to open in Excel smoothly (including Mac excel)
So there is an answer here: How can I output a UTF-8 CSV in PHP that Excel will read properly?
Checkout the top answer.
But then in order to implement that you need to use tabs to separate the fields instead of commas...Is there a way to achieve this while still using commas and not tabs and still have it work in OS X
EDIT
Mostly to Mark Baker but everyone feel free to comment
Another code update
while(#ob_end_clean());
header('Content-Encoding: UTF-8');
header('Content-type: text/csv; charset=UTF-8');
header("Cache-Control: no-store, no-cache");
header("Content-Disposition: attachment; filename=fileexport.csv");
echo "\xEF\xBB\xBF";
print "sep=,\n";
print $output;
exit;
fputcsv should work fine in this instance. Take the following example, where as the third parameter of fputcsv is the delimiter. By default it is , (comma), but you could also use "\t" for tab files. CSV files should be interpreted the same on either OS
if( $fh = fopen("output_file.csv","w") ){
$put = array("column1, with comma","column2, with comma","column3" /*,"columnN"*/);
fputcsv($fh,$put,",");
fclose($fh);
}
I've been looking through all the answers here and I haven't found the solution.
Here's what I got:
MySQL :
Database & Table encoding => utf8_unicode_ci
I'm trying to convert a an array (containing rows from a query) to CSV
however when i open the csv I get this
Prénom
instead of
Prénom
here's my code
$allQueryRows = array();
while($row_query = $stmt_select->fetch(PDO::FETCH_ASSOC)){
$row_query = array_map("utf8_encode", $row_query);
array_push($allQueryRows, $row_query);
}
download_send_headers("csv" . date("Y-m-d") . ".csv");
echo array2csv($allQueryRows);
die();
function array2csv(array &$array)
{
if (count($array) == 0) {
return null;
}
ob_start();
$df = fopen("php://output", 'w');
fputcsv($df, array_keys(reset($array)));
foreach ($array as $row) {
fputcsv($df, $row);
}
fclose($df);
return ob_get_clean();
}
function download_send_headers($filename) {
// disable caching
$now = gmdate("D, d M Y H:i:s");
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: {$now} GMT");
// force download
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
// disposition / encoding on response body
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
}
You should send a SET NAMES utf8; MySQL query before your SELECT query instead of array_mapping your data after.
Then in HTTP headers, send
Content-Type: text/csv; charset=utf-8;
AT first blush it looks like a utf-16 / utf-8 issue. Here's how to start to diagnose it:
First, when you say, "however when i open the csv I get this" what do you mean by "open", i.e. open with what?
I would suggest looking at the file in hex to see exactly what is in the file. It could be that what you are opening it in (your editor or whatever) is what is causing the display coming into your eye ball to be wrong OR it could be that the underlying data that your opening program is seeing is wrong. I think you need to sort out that question first.
(Tip: In general this is the old process of divide and conquer: find a way to test somewhere in the middle of your problem to see which half of your system is causing the problem. The quickest results come from picking test points about half way in the middle of the complexity, not near an edge of the problem, i.e. a Boolean search for the bug. It might not find the problem in the first iteration, but it will help narrow it down.)
Also perhaps you need to tell SQL which to use, e.g. $connection->set_charset("utf8");
Or perhaps what you are seeing is actually being displayed differently from what you think it is because of a utf8/utf16 display level mixup. I generally set stay with utf8 and so set Content-Type: text/plain; charset=UTF-8; (Also if you are viewing this file via your editor make sure it's set to the correct character space.)
I am looking for a simple way to take an array, turn it into a CSV and have a user be able to download the CSV, all without saving the CSV to the server.
$array = [
['name', 'email'],
['Doe, John', 'johndoe#foo'],
['Jane Doe', 'janedoe#foo'],
['Ron "SuperFly" O\'Neal', 'supafly#foo'],
];
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=test.csv");
$fp = fopen('php://output', 'w'); // or use php://stdout
foreach ($array as $row) {
fputcsv($fp, $row);
}
see fputcsv()
The benefit of using fputcsv is it will handle escaping and enclosing of fields for you, ensuring valid, rugged output that will never break even if your columns values happen to contain csv control characters such as commas, quotes, newline characters etc... If you don't take care to handle control characters robustly, and instead take a common shortcut such as using implode(',', $row) your (low quality) code will produce broken csv output.
Unfortunately, fputcsv cant output to a string, only to a stream, so I write to php's output stream via php://output which is equivalent to echo'ing it out. Note that just like output via echo, it will go through php's output buffer mechanism, and that includes any possible output handlers, such as ob_gzhandler or customer output buffer callback functions that may modify the output. If you wish to bypass the output buffer, you can instead write directly to php://stdout.
You could also write to php://memory, php://temp, or a file if you need to access the csv file contents as a string before outputting it.
$fp = fopen('php://memory', 'w+'); // or php://temp
foreach ($array as $row) {
fputcsv($fp, $row);
}
// Read it back into a string, if desired.
// Rewind the stream to start.
fseek($fp, 0);
// Read the entire stream back to string.
$csvContents = stream_get_contents($fp);
info on stream wrappers http://www.php.net/manual/en/wrappers.php.php
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=test.csv");
header("Pragma: no-cache");
header("Expires: 0");
echo "name,city,street\n";
Sometimes it's better to use
header("Content-Type: application/octet-stream");
for IE...
I am having issues importing a CSV file which contains (french) names with accents in them... when ever they are imported the accent do not display properly example
félix turns into fŽlix
the file is created by hand and then imported into PHP.
I have tried both utf8_encode() and utf8_decode() and nether function will convert the chars so they can be viewed properly.
my question is how can i get this to render properly... convert char-set.. etc
I believe the text is encoded in Cp850 based on other questions i've seen on here. I am using fgetcvs() to get the contents.
Set Header Information before you output as UTF
header('Content-Type: text/html; charset=utf-8');
$log = file_get_contents("log.csv");
echo utf8_encode($log);
Output
félix
Please, try iconv() function
I think this is late answer but may be helpful for those who are still searching for solution. This is just a tweak. Not always recommended .
header('Content-Encoding: UTF-8');
header('Content-type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename=filename.csv');
echo "\xEF\xBB\xBF"; // UTF-8 with BOM
readfile("filename.csv");
exit;
I'm doing this on upload
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_dir .$target_file)) {
$log = file_get_contents($target_dir .$target_file);
file_put_contents($target_dir .$target_file, utf8_encode($log));
The following code gets a 'report line' as an array and uses fputcsv to tranform it into CSV. Everything is working great except for the fact that regardless of the charset I use, it is putting a UTF-8 bom at the beginning of the file. This is exceptionally annoying because A) I am specifying iso and B) We have lots of users using tools that show the UTF-8 bom as characters of garbage.
I have even tried writing the results to a string, stripping the UTF-8 BOM and then echo'ing it out and still get it. Is it possible that the issue resides with Apache?
If I change the fopen to a local file it writes it just fine without the UTF-8 BOM.
header("Content-type: text/csv; charset=iso-8859-1");
header("Cache-Control: no-store, no-cache");
header("Content-Disposition: attachment; filename=\"report.csv\"");
$outstream = fopen("php://output",'w');
for($i = 0; $i < $report->rowCount; $i++) {
fputcsv($outstream, $report->getTaxMatrixLineValues($i), ',', '"');
}
fclose($outstream);
exit;
My guess would be that your php source code file has a BOM, and you have php's output buffering enabled.
I don't know if this solves your problem but have you tried using the print and implode functions to do the same thing?
header("Content-type: text/csv; charset=iso-8859-1");
header("Cache-Control: no-store, no-cache");
header("Content-Disposition: attachment; filename=\"report.csv\"");
for($i = 0; $i < $report->rowCount; $i++) {
print(implode(',',$report->getTaxMatrixLineValues($i)));
}
That's not tested but pretty obvious.