如何在PHP中输出一个Excel可以正确读取的UTF-8格式的CSV文件?

225

我有一个非常简单的东西,只是以CSV格式输出一些内容,但它必须是UTF-8编码。我在TextEdit、TextMate或Dreamweaver中打开此文件时,它会正确显示UTF-8字符,但如果我在Excel中打开它,它会显示像íÄ这样的奇怪字符。以下是我文档头部所写的内容:

header("content-type:application/csv;charset=UTF-8");
header("Content-Disposition:attachment;filename=\"CHS.csv\"");

除了 Excel(Mac,2008)无法正确导入之外,所有这些似乎都有所作用。Excel中没有选项可以让我“以UTF-8格式打开”或任何其他选项,所以我有点烦恼。

我似乎找不到任何清晰的解决方案,尽管很多人都遇到了同样的问题。我看到的最多的是包含BOM,但我无法确定如何做到这一点。正如您在上面看到的,我只是“echo”这些数据,我没有写任何文件。如果需要,我可以这样做,只是因为目前似乎没有必要。有帮助吗?

更新:我尝试将 BOM 作为 echo pack("CCC", 0xef, 0xbb, 0xbf); 传递,这仅仅是从一个试图检测 BOM 的网站上获取的。但是,当它导入时,Excel只是将这三个字符附加到第一个单元格,并仍然会弄乱特殊字符。


2
Excel没有提供调整输入文件字符集的选项吗?你确定100%吗?我手边没有副本,所以无法尝试,但我想象中一定有一个下拉框。 - Pekka
这是在 Mac 上的 Excel - 它似乎比 PC 上的 Excel 有更多限制。在“打开”对话框中根本没有下拉菜单,只能选择要打开的文件类型。我已经到处找了,如果它存在的话,那就很难发现。我可以说有 98% 的把握。 - Ben Saufley
微软办公套件还是开源办公套件? - ajreal
微软在这方面更好,我找不到任何方法让OpenOffice检测字符集,甚至是BOM。真可惜。 - Ciantic
BOM对于Mac版的Microsoft Excel 2008也没有影响。 - Ben Saufley
26个回答

7

添加:

fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));

或者:

fprintf($file, "\xEF\xBB\xBF");

在将任何内容写入CSV文件之前,例如:
<?php
$file = fopen( "file.csv", "w");
fprintf( $file, "\xEF\xBB\xBF");
fputcsv( $file, ["english", 122, "বাংলা"]);
fclose($file);

6

使用mb_convert_encoding转换已经是utf-8编码的文本是不必要的。只需在原始内容前添加三个字符即可:

$newContent = chr(239) . chr(187) . chr(191) . $originalContent

对我来说,这解决了csv文件中特殊字符的问题。


1
难以置信;我尝试了至少10种其他的解决方案,但只有这个方案似乎适合我,当与UTF-8 BOM通过fputs结合使用时。 - kaarto
这些字符是什么?一定是某种魔法咒语。在尝试了这里的每一个其他答案之后,唯一有效的解决方案。 - leymannx
1
这些字符组合被称为“字节对象标记”,它告诉检查它的应用程序其余内容应该被视为UTF-8。 - Rvanlaak

5

由于UTF8编码与Excel不兼容,您可以使用iconv()将数据转换为其他编码类型。

例如:

iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $value),

这是我在Excel Mac 2011中唯一可行的解决方案。 - Christer Fernstrom

3
**This is 100% works fine in excel for both Windows7,8,10 and also All Mac OS.**
//Fix issues in excel that are not displaying characters containing diacritics, cyrillic letters, Greek letter and currency symbols.

function generateCSVFile($filename, $headings, $data) {

    //Use tab as field separator
    $newTab  = "\t";
    $newLine  = "\n";

    $fputcsv  =  count($headings) ? '"'. implode('"'.$newTab.'"', $headings).'"'.$newLine : '';

    // Loop over the * to export
    if (! empty($data)) {
      foreach($data as $item) {
        $fputcsv .= '"'. implode('"'.$newTab.'"', $item).'"'.$newLine;
      }
    }

    //Convert CSV to UTF-16
    $encoded_csv = mb_convert_encoding($fputcsv, 'UTF-16LE', 'UTF-8');

    // Output CSV-specific headers
    header('Set-Cookie: fileDownload=true; path=/'); //This cookie is needed in order to trigger the success window.
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false);
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$filename.csv\";" );
    header("Content-Transfer-Encoding: binary");
    header('Content-Length: '. strlen($encoded_csv));
    echo chr(255) . chr(254) . $encoded_csv; //php array convert to csv/excel
    exit;
}

2
通常情况下,如果答案包含代码的意图和解决问题的原因,而不会引入其他问题,那么这些答案会更有帮助。 - Tim Diekmann

2
您可以使用iconv将CSV字符串进行转换。 例如:
$csvString = "Möckmühl;in Möckmühl ist die Hölle los\n";
file_put_contents('path/newTest.csv',iconv("UTF-8", "ISO-8859-1//TRANSLIT",$csvString) );

很棒的示例文本 ;) - ngeek

2

在我的调查中,我发现UTF-8在MAC和Windows上运行效果不佳,因此我尝试了Windows-1252,它在这两个系统上都支持良好,但你必须在Ubuntu上选择编码类型。以下是我的代码:$valueToWrite = mb_convert_encoding($value, 'Windows-1252');

$response->headers->set('Content-Type', $mime . '; charset=Windows-1252');
    $response->headers->set('Pragma', 'public');
    $response->headers->set('Content-Endcoding','Windows-1252');
    $response->headers->set('Cache-Control', 'maxage=1');
    $response->headers->set('Content-Disposition', $dispositionHeader);
    echo "\xEF\xBB\xBF"; // UTF-8 BOM

1
你必须使用编码"Windows-1252"。
// NO NO NO header('Content-Encoding: Windows-1252');
header('Content-type: text/csv; charset=Windows-1252');
header("Content-Disposition: attachment; filename={$filename}");

也许你需要转换你的字符串:
private function convertToWindowsCharset($string) {
  $encoding = mb_detect_encoding($string);

  return iconv($encoding, "Windows-1252", $string);
}

Content-Encoding应该不会使用文本编码值,如"Windows-1252"。它与压缩有关。有效的值为{gzip, br, deflate}。https://dev59.com/uGQn5IYBdhLWcg3wCjX0#17155003 - Cheeso

1

对我来说,上面提供的解决方案都没有起作用。以下是我解决这个问题的方法:在PHP代码中使用此函数修改值:

$value = utf8_encode($value);

这在 Excel 表格中可以正确输出数值。

1

我在Mac上,我只需要用"sep=;\n"指定分隔符,并将文件编码为UTF-16LE,就像这样:

$data = "sep=;\n" .mb_convert_encoding($data, 'UTF-16LE', 'UTF-8');

1

在导出文件之前,您可以将这3个字节附加到文件中,这对我很有效。在这样做之前,该系统仅在Windows和HP-UX上运行,但在Linux上失败。

FileOutputStream fStream = new FileOutputStream( f );
final byte[] bom = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
OutputStreamWriter writer = new OutputStreamWriter( fStream, "UTF8" );
fStream.write( bom );

在文件开头加上UTF-8 BOM(3字节,十六进制EF BB BF)。否则,Excel会根据您所在区域的默认编码(例如cp1252)解释数据,而不是utf-8。 生成Excel的CSV文件,如何在值内部换行

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接