从 mysql 数据库生成的可下载 csv 文件中的 Unicode 字符不可读
Unicode characters not readable in downloadable csv file generated from mysql database
我正在尝试使用 php 脚本生成从 mysql 数据库生成的 csv 可下载文件。它正在工作,但其中的 unicode 字符不可读。当我在 Notepad++ 中打开时,unicode 字符是可读的。我阅读了有关此问题的答案,但没有帮助。请帮忙。以下是我的代码 -
<?php
mb_internal_encoding("UTF-8");
mb_http_output( "UTF-8" );
ob_start("mb_output_handler");
include("t/db_config.php");
$con=mysqli_connect($db_host,$db_user,$db_password,$db_name);
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//set_charset when connecting with database
mysqli_set_charset( $con, 'utf8');
$data=array();
$sql="SELECT s.t_id,s.t_text,p.user_name,p.description,s.time,p.place from
t
AS s INNER JOIN users AS p ON s.user_name=p.user_name order by s.time
desc";
$result = mysqli_query($con,$sql);
while($row = mysqli_fetch_array($result)) {
$array=array("Link" => $row[0],"Text"=>$row[1] , "User Name" => $row[2]
, "User Profile" => $row[3], "Time" => $row[4] , "Place" => $row[5]);
array_push($data,$array);
}
function cleanData(&$str)
{
if($str == 't') $str = 'TRUE';
if($str == 'f') $str = 'FALSE';
if(preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) ||
preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
$str = "$str";
}
if(strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
}
// filename for download
$filename = "website_data_" . date('Ymd') . ".csv";
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Type: text/csv");
$out = fopen("php://output", 'w');
$flag = false;
foreach($data as $row) {
if(!$flag) {
// display field/column names as first row
fputcsv($out, array_keys($row), ',', '"');
$flag = true;
}
array_walk($row, 'cleanData');
fputcsv($out, array_values($row), ',', '"');
}
fclose($out);
exit;
?>
这是一个示例输入,也是所需的输出 -
umeshdutt निर्दोष को सजा मिल रही है
但是在 excel -
中打开 csv 文件后得到以下输出
umeshdutt कमाल का कानून है जि मे नि ¤°à¤¹à¥€ हà¥^।
编辑-
Mysql table 具有样本数据的结构
Table t
1)t_id (初级.....|2).....t_text......|3).....时间... .....|4)..user_name........
===================|============================== =========================
1)bigint(20)......| 2) 变量 (255) | 3)日期时间 | 4)字符(20)
...................| 2) utf8_general_ci | | 4)utf8_general_ci
===================|=================|============ =========================
847589475442204000 | संविधान 'सुप्रीम' है | 2017 年 3 月 31 日 5:01:52 上午 |科蒂安人
Table 用户
1) user_id(小学) | 2)user_name | 3)地点 | 4)描述
==================|================|============== =|============
1) 大整数(20) |2) 字符(20) | 3) 可变字符(30) |4) 可变字符(200)
|2) utf8_general_ci |3) utf8_general_ci|4) utf8_general_ci
==================|================|============== =|=============
2883542694 |科蒂安人 |阿德莱德 |工程师
这是 运行 成功的完整 PHP 代码(诚然,我没有花时间系统地删除 encoding
和 header
函数以查看是否它仍然可以使用更少的代码):
if(!$con=mysqli_connect("host","user","pass","db")){
echo "Failed to connect to MySQL: ",mysqli_connect_error();
}else{
mysqli_set_charset($con,'utf8');
$sql="SELECT
CONCAT('=\"',t.t_id,'\"'),
t.t_text,
p.user_name,
p.description,
CONCAT('=\"',t.time,'\"'),
p.place
FROM `t`
INNER JOIN `users` p ON t.user_name=p.user_name
ORDER BY t.time DESC;";
if($result=mysqli_query($con,$sql)){
header("Content-Disposition: attachment; filename=\"website_data_".date('Ymd').".csv\"");
header("Content-Type: text/csv");
header('Pragma: no-cache');
header('Expires: 0');
$out=fopen('php://output','w');
fputs($out,"\xEF\xBB\xBF"); // Byte Order Mark
fputcsv($out,["Link","Text","User Name","User Profile","Time","Place"],',','"');
while($row=mysqli_fetch_row($result)){
fputcsv($out,$row,',','"');
}
fclose($out);
}else{
echo mysqli_error($con);
}
}
在Excel中默认[=15=]的大整数值将使用科学记数法(8.47589E+17
)显示,t.time
的格式将变为:n/j/Y g:i:s
为了掩盖这些默认调整,我将值用双引号 ("
) 括起来,并在每个值前面加上 =
.
我建议在 sql 中执行任何数据库值 cleaning/modifying,因为您可以准备特定的列来处理已知问题,而不是迭代行中的所有值。
"Byte Order Mark" 是对原始代码的重要补充。
看来至少可以这样写,这三种写法效果一样:
fputs($out,chr(0xEF).chr(0xBB).chr(0xBF));
fputs($out,chr(239).chr(187).chr(191));
fputs($out,"\xEF\xBB\xBF"); // I chose the shortest one
参考资料和补充阅读:
- https://www.w3.org/International/questions/qa-byte-order-mark
- https://en.wikipedia.org/wiki/Byte_order_mark
- https://www.skoumal.net/en/making-utf-8-csv-excel/
我收录了几个 suggestions/refinements 比如:
- 在继续生成 csv 之前检查非假
$result
。
- 添加了几个额外的 header() 语句以确保新鲜度。
fputcsv()
在循环之前静态地编辑了键数组。
- 简化了
while()
循环中的过程。
我使用了这些表中的数据:
CREATE TABLE `t` (
`t_id` bigint(20) NOT NULL,
`t_text` varchar(255) CHARACTER SET utf8 NOT NULL,
`time` datetime NOT NULL,
`user_name` char(20) CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `t` (`t_id`, `t_text`, `time`, `user_name`) VALUES
(847589475442204000, 'संविधान \'सुप्रीम\' है', '2017-03-01 05:01:52', 'kotians');
ALTER TABLE `t` ADD PRIMARY KEY (`t_id`);
CREATE TABLE `users` (
`user_id` bigint(20) NOT NULL,
`user_name` char(20) CHARACTER SET utf8 NOT NULL,
`place` varchar(30) CHARACTER SET utf8 NOT NULL,
`description` varchar(200) CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `users` (`user_id`, `user_name`, `place`, `description`) VALUES
(2883542694, 'kotians', 'Ade\'laide', 'Engi\"neer');
ALTER TABLE `users` ADD PRIMARY KEY (`user_id`);
这是生成的 CSV 文件中活动单元格的屏幕截图:
我正在尝试使用 php 脚本生成从 mysql 数据库生成的 csv 可下载文件。它正在工作,但其中的 unicode 字符不可读。当我在 Notepad++ 中打开时,unicode 字符是可读的。我阅读了有关此问题的答案,但没有帮助。请帮忙。以下是我的代码 -
<?php
mb_internal_encoding("UTF-8");
mb_http_output( "UTF-8" );
ob_start("mb_output_handler");
include("t/db_config.php");
$con=mysqli_connect($db_host,$db_user,$db_password,$db_name);
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//set_charset when connecting with database
mysqli_set_charset( $con, 'utf8');
$data=array();
$sql="SELECT s.t_id,s.t_text,p.user_name,p.description,s.time,p.place from
t
AS s INNER JOIN users AS p ON s.user_name=p.user_name order by s.time
desc";
$result = mysqli_query($con,$sql);
while($row = mysqli_fetch_array($result)) {
$array=array("Link" => $row[0],"Text"=>$row[1] , "User Name" => $row[2]
, "User Profile" => $row[3], "Time" => $row[4] , "Place" => $row[5]);
array_push($data,$array);
}
function cleanData(&$str)
{
if($str == 't') $str = 'TRUE';
if($str == 'f') $str = 'FALSE';
if(preg_match("/^0/", $str) || preg_match("/^\+?\d{8,}$/", $str) ||
preg_match("/^\d{4}.\d{1,2}.\d{1,2}/", $str)) {
$str = "$str";
}
if(strstr($str, '"')) $str = '"' . str_replace('"', '""', $str) . '"';
}
// filename for download
$filename = "website_data_" . date('Ymd') . ".csv";
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Type: text/csv");
$out = fopen("php://output", 'w');
$flag = false;
foreach($data as $row) {
if(!$flag) {
// display field/column names as first row
fputcsv($out, array_keys($row), ',', '"');
$flag = true;
}
array_walk($row, 'cleanData');
fputcsv($out, array_values($row), ',', '"');
}
fclose($out);
exit;
?>
这是一个示例输入,也是所需的输出 -
umeshdutt निर्दोष को सजा मिल रही है
但是在 excel -
中打开 csv 文件后得到以下输出umeshdutt कमाल का कानून है जि मे नि ¤°à¤¹à¥€ हà¥^।
编辑-
Mysql table 具有样本数据的结构
Table t
1)t_id (初级.....|2).....t_text......|3).....时间... .....|4)..user_name........
===================|============================== =========================
1)bigint(20)......| 2) 变量 (255) | 3)日期时间 | 4)字符(20)
...................| 2) utf8_general_ci | | 4)utf8_general_ci
===================|=================|============ =========================
847589475442204000 | संविधान 'सुप्रीम' है | 2017 年 3 月 31 日 5:01:52 上午 |科蒂安人
Table 用户
1) user_id(小学) | 2)user_name | 3)地点 | 4)描述
==================|================|============== =|============
1) 大整数(20) |2) 字符(20) | 3) 可变字符(30) |4) 可变字符(200)
|2) utf8_general_ci |3) utf8_general_ci|4) utf8_general_ci
==================|================|============== =|=============
2883542694 |科蒂安人 |阿德莱德 |工程师
这是 运行 成功的完整 PHP 代码(诚然,我没有花时间系统地删除 encoding
和 header
函数以查看是否它仍然可以使用更少的代码):
if(!$con=mysqli_connect("host","user","pass","db")){
echo "Failed to connect to MySQL: ",mysqli_connect_error();
}else{
mysqli_set_charset($con,'utf8');
$sql="SELECT
CONCAT('=\"',t.t_id,'\"'),
t.t_text,
p.user_name,
p.description,
CONCAT('=\"',t.time,'\"'),
p.place
FROM `t`
INNER JOIN `users` p ON t.user_name=p.user_name
ORDER BY t.time DESC;";
if($result=mysqli_query($con,$sql)){
header("Content-Disposition: attachment; filename=\"website_data_".date('Ymd').".csv\"");
header("Content-Type: text/csv");
header('Pragma: no-cache');
header('Expires: 0');
$out=fopen('php://output','w');
fputs($out,"\xEF\xBB\xBF"); // Byte Order Mark
fputcsv($out,["Link","Text","User Name","User Profile","Time","Place"],',','"');
while($row=mysqli_fetch_row($result)){
fputcsv($out,$row,',','"');
}
fclose($out);
}else{
echo mysqli_error($con);
}
}
在Excel中默认[=15=]的大整数值将使用科学记数法(8.47589E+17
)显示,t.time
的格式将变为:n/j/Y g:i:s
为了掩盖这些默认调整,我将值用双引号 ("
) 括起来,并在每个值前面加上 =
.
我建议在 sql 中执行任何数据库值 cleaning/modifying,因为您可以准备特定的列来处理已知问题,而不是迭代行中的所有值。
"Byte Order Mark" 是对原始代码的重要补充。
看来至少可以这样写,这三种写法效果一样:
fputs($out,chr(0xEF).chr(0xBB).chr(0xBF));
fputs($out,chr(239).chr(187).chr(191));
fputs($out,"\xEF\xBB\xBF"); // I chose the shortest one
参考资料和补充阅读:
- https://www.w3.org/International/questions/qa-byte-order-mark
- https://en.wikipedia.org/wiki/Byte_order_mark
- https://www.skoumal.net/en/making-utf-8-csv-excel/
我收录了几个 suggestions/refinements 比如:
- 在继续生成 csv 之前检查非假
$result
。 - 添加了几个额外的 header() 语句以确保新鲜度。
fputcsv()
在循环之前静态地编辑了键数组。- 简化了
while()
循环中的过程。
我使用了这些表中的数据:
CREATE TABLE `t` (
`t_id` bigint(20) NOT NULL,
`t_text` varchar(255) CHARACTER SET utf8 NOT NULL,
`time` datetime NOT NULL,
`user_name` char(20) CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `t` (`t_id`, `t_text`, `time`, `user_name`) VALUES
(847589475442204000, 'संविधान \'सुप्रीम\' है', '2017-03-01 05:01:52', 'kotians');
ALTER TABLE `t` ADD PRIMARY KEY (`t_id`);
CREATE TABLE `users` (
`user_id` bigint(20) NOT NULL,
`user_name` char(20) CHARACTER SET utf8 NOT NULL,
`place` varchar(30) CHARACTER SET utf8 NOT NULL,
`description` varchar(200) CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `users` (`user_id`, `user_name`, `place`, `description`) VALUES
(2883542694, 'kotians', 'Ade\'laide', 'Engi\"neer');
ALTER TABLE `users` ADD PRIMARY KEY (`user_id`);
这是生成的 CSV 文件中活动单元格的屏幕截图: