【问题标题】:PHP export CSV UTF-8 with BOM doesn't work带有 BOM 的 PHP 导出 CSV UTF-8 不起作用
【发布时间】:2017-03-19 17:49:25
【问题描述】:

我在导出带有中文字符的 UTF-8 CSV 时遇到了几天,这些字符在 Windows Excel 上显示乱码。我正在使用 PHP,并且已经添加了 BOM 字节标记并尝试了编码,但一点运气都没有。

它们可以在 Notepad++、Google 电子表格甚至 Mac Numbers 上正常打开。但不是在客户要求的 Excel 上。使用 Notepad++ 打开时,编码显示为 UTF-8。如果我手动将其更改为 UTF-8 并保存,则该文件在 Excel 上可以正常打开。

似乎 BOM 字节标记没有保存在输出中,因为 Notepad++ 总是将其检测为没有 BOM 的 UTF-8。

此外,CSV 不会保存在服务器上。数据从数据库中检索出来,然后直接导出。

这是我的代码:

// Setup headers
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Description: File Transfer');
header("Content-type: text/csv");
header("Content-disposition: filename=".$filename.".csv");
header("Pragma: no-cache");

// First Method
$fp = fopen('php://output', 'w');
// Add BOM to fix UTF-8 in Excel, but doesn't work
fputs($fp, chr(0xEF) . chr(0xBB) . chr(0xBF) );

if ($fp) {

    fputcsv($fp, array("Header"), ",");
    fputcsv($fp, array($string_with_chinese_chars), ",");
}

fclose($fp);
exit();

// Second Method
$csv = "";
$sep = ",";
$newline = "\n"; // Also tried with PHP_EOL

$csv .= "Header";
$csv .= $newline;
$csv .= $string_with_chinese_chars;
$csv .= $newline;

// Tried all the below ways but doesn't work.
// Method 2.1
print chr(255) . chr(254) . mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');

// Method 2.2
print chr(239) . chr(187) . chr(191) . $csv;

// Method 2.3
print chr(0xEF).chr(0xBB).chr(0xBF);
print $newline;
print $csv;

【问题讨论】:

  • 你能在Notepad++中保存之前和之后在十六进制编辑器中打开文件,看看有什么区别吗?如果它足够短,甚至可以将文件的十六进制转储添加到您的问题中?
  • 好的,我试试再更新。
  • 更新:下载的文件HEX以0A EF BB BF开头。而使用 Notepad++ 保存后的文件以 EF BB BF 0A EF BB BF 开头。 0A 看起来是一条新线。不知何故,这似乎是在文件的开头添加的,即使代码中没有任何部分这样做。这是一个共享托管服务器,我无权访问 php.ini。

标签: php excel csv encoding utf-8


【解决方案1】:

根据您上面的评论,您的脚本似乎在 UTF-8 BOM 之前意外打印出换行符(十六进制 0A),导致 Excel 无法将输出识别为 UTF-8。

由于您使用的是 PHP,因此请确保您的脚本或任何其他可能包含的 PHP 文件中的 <?php 标记之前没有空行。还要确保您包含的所有文件结束 ?> 标记之后没有任何空格(如果有的话)。

实际上,这可能很难做到,因为许多文本编辑器坚持总是在最后一行的末尾附加一个换行符。因此,最安全和最简单的解决方案是简单地leave out the ?> marker from your PHP files,除非您打算打印出它后面的任何内容。 PHP 不需要 ?> 存在,并且在不打算混合 PHP 和文字模板 HTML(或其他文本)的文件中使用它只是要求这样的错误。

【讨论】:

  • 我可能忘了补充一点,即代码是我为 Wordpress 开发的插件的一部分。我很确定我的插件在
  • 这可能确实是必要的。我要做的第一件事是对?> 的所有 PHP 文件进行 grep,然后将其从任何不需要它的文件中删除(即后面没有任何实际 HTML 或其他内容的文件)。如果这不能解决问题,编写一个脚本来遍历所有文件并检查每个文件的前五个字节是否为<?php 也不难。您也许也可以使用输出缓冲(即在缓冲区中捕获杂散的换行符并将其丢弃)来解决此问题,如果您能尽早获得ob_start()。但我只会在万不得已的情况下这样做。
  • 听起来工作量很大。我会试试看。 php.ini中的配置是否也有可能导致这种情况?我可能不得不编写一个独立的 PHP 脚本,看看是否会发生同样的事情。
  • 如果你碰巧安装了 perl,perl -0777 -nE 'say $ARGV if /^\s+<\?/ or /\?>\s+$/' *.php 应该列出当前目录中所有在第一个 <? 之前或最后一个 ?> 之后有空格的 .php 文件.要搜索子目录,请尝试例如find . -name '*.php' -exec perl -0777 -nE 'say $ARGV if /^\s+<\?/ or /\?>\s+$/' '{}' ';'。 (这应该适用于 bash、perl 和 GNU find,它们默认安装在 Linux 上,但也可用于 Windows,例如通过 Git for Windows。)
  • 我会试试 perl 脚本。同时,我使用了 ob_start() 但似乎角色仍然存在。如何使用 ob_start 擦除缓冲区中的任何内容并重新开始?
【解决方案2】:

以下代码对我有用。在 csv 内容之前输出 utf-8-bom 字符:

  echo "\xEF\xBB\xBF"; // utf-8 bom 
  echo $csv;

【讨论】:

  • 谢谢!我正在编写欧洲字符以在没有 BOM 的情况下输出,这个解决方案运行良好,现在在 Excel 中我看到像 Reneé 这样的名称正确显示
【解决方案3】:

希望这可以帮助某人。 对我有用的是我必须把两者都放进去:

...
echo chr(0xEF) . chr(0xBB) . chr(0xBF);
$file = fopen('php://output', 'w');
fputs($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
...

我不是 PHP 方面的专家,所以我无法解释为什么这样做我希望这对某人有所帮助,因为我也很难解决这个问题。

【讨论】:

  • 不知道为什么会这样,但确实如此。请注意,它似乎只需要两次 bom,它可以在同一行重复。
  • 我花了一天的时间,谢谢你!这让我认为它与写入 php 输出而不是直接写入文件有关,因为它可以在没有重复的情况下工作。如果有人能解释一下就好了。
【解决方案4】:

我通常这样做:

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="filename.csv"');
header('Cache-Control: max-age=0');

// BOM header UTF-8
echo "\xEF\xBB\xBF";

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

...

我使用";" 作为分隔符,因为 excel 大多数可能不会自动格式化","

【讨论】:

  • 我也试过了,但它不起作用。另外,只是补充一下,如果我将文件保存到服务器并直接从服务器下载,它可以工作。
猜你喜欢
  • 2016-05-04
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 2012-02-12
  • 2011-10-03
  • 2014-03-10
  • 2011-02-04
相关资源
最近更新 更多