Microsoft Excel在.csv文件中破坏了音标符号?

207

我通过 PHP 5.2 编程方式将数据导出到 .csv 测试文件中。
例如数据:Numéro 1(请注意重音符号的e)。 数据是 utf-8 格式(没有前置 BOM)。

当我在 MS Excel 中打开此文件时,它显示为 Numéro 1

我能够在文本编辑器(UltraEdit)中打开并正确显示。 UE 报告该字符为 十进制 233

我该如何导出文本数据到 .csv 文件中,以便 MS Excel 能够正确呈现,最好不需要强制使用导入向导或非默认向导设置?


3
选择的工作解决方案是:
  • 包括一个BOM; utf-8
  • 使用此标题:'Content-type: text/plain; charset=utf-8'
这在Excel 2003和Excel 2007上“可行”--其中“可行”=无需导入向导即可打开并正确呈现变音符号。我没有验证是否需要BOM。
- Freddo411
2
需要BOM,我刚刚测试过了。没有它,特殊字符无法正确显示。 - Alex Ciminian
2
希望有人能够更详细地说明如何添加BOM(字节顺序标记)。如果我只是像Response.Write(EF BB BF")这样做,那么这些字符就会出现在文件的开头。 - sydneyos
请查看以下链接: https://dev59.com/BGIk5IYBdhLWcg3wNrq8#20952812 这对我也有效。 - jacob spitzer
看看这个: https://dev59.com/BGIk5IYBdhLWcg3wNrq8#20952812 对我来说非常好用。 - jacob spitzer
显示剩余3条评论
22个回答

267
一个正确格式的UTF8文件可以在其前三个八位字节中有一个字节顺序标记。这些十六进制值为0xEF,0xBB,0xBF。这些八位字节用于标记文件为UTF8(因为它们与“字节顺序”信息无关)。1如果不存在该BOM,则消费者/读者必须推断文本的编码类型。不支持UTF8的读者将把这些字节读取为其他编码(如Windows-1252),并在文件开头显示字符
已知存在一个错误,即Excel通过文件关联打开UTF8 CSV文件时,假定它们是单字节编码,而忽略了UTF8 BOM的存在。任何系统默认代码页或语言设置都无法修复此问题。BOM不会提示Excel - 它只是无法正常工作。(少数报告声称BOM有时会触发“导入文本”向导。)此错误似乎存在于Excel 2003及更早版本中。大多数报告(在这里的答案中)说这在Excel 2007及更高版本中得到了解决。
请注意,您始终可以*使用“导入文本”向导正确打开UTF8 CSV文件,在其中指定要打开的文件的编码。当然,这样做就不太方便了。
读者们可能处于这样一种情况:他们并不特别支持 Excel < 2007,但是却向 Excel 发送了原始的 UTF8 文本,Excel 对其进行了错误解释,并在您的文本中添加了 Ã 和其他类似的 Windows-1252 字符。 添加 UTF8 BOM 可能是您最好和最快的解决方法。 如果您被困在旧版 Excel 上,而且 Excel 是您 CSV 文件的唯一消费者,那么您可以通过导出 UTF16 而不是 UTF8 来解决这个问题。Excel 2000 和 2003 可以正确地打开这些文件。(某些其他文本编辑器可能会有 UTF16 的问题,因此您需要仔细权衡您的选择。)

* 除非您无法这样做,否则至少Excel 2011 for Mac的导入向导实际上并不总是使用所有编码工作,无论您告诉它什么。 </anecdotal-evidence> :)


15
找到指定编码的地方花费了我很长时间。保存对话框 > 工具按钮 > Web选项 > 编码标签页。他们真的很擅长隐藏这样重要的事情。 - Triynko
7
如果在UTF-8文件中添加BOM,则可以在Excel 2007中正确加载该文件,而无需使用导入向导。 - Victor Nicollet
22
通常情况下,UTF-8编码的文件应该不要在开头添加字节顺序标记(Byte Order Mark,BOM)。UTF-8没有可变的字节顺序,并且将其放在那里会破坏UTF-8与ASCII的兼容性。有一些特定的文件格式可能允许或鼓励使用UTF-8伪BOM,但除此之外应该避免使用。CSV文件完全不考虑编码问题,因此对于一个给定的工具是否将字节序列0xEF 0xBB 0xBF解释为UTF-8指示符、第一个单元格中的不可见控制字符、第一个单元格中的字符“”,还是其他内容,任何人都无从得知。 - bobince
4
@Ian:没有人确切知道这是带有BOM的UTF-8编码——0xEF 0xBB 0xBF 在大多数旧编码中也是有效的序列(因此它经常被误解为ISO-8859-1或cp1252,并显示为)。它只有在猜测算法和某些明确允许其使用的文件格式(例如XML)才有所帮助。在UTF-8文件中包含伪BOM的缺点是会破坏它们与ASCII的兼容性(这是UTF-8的一个重要卖点)。很多不考虑编码的文本工具会在遇到意外的前导伪BOM时出现错误。 - bobince
3
UTF-16LE+它的BOM对我也起了作用,并且解决了在双击打开CSV文件时出现的编码问题。但对我来说,以制表符作为分隔符(而不是逗号)是不期望的行为。是否有办法更改这一点? - me1111
显示剩余20条评论

44
在编程中,将BOM(\uFEFF)添加到文件开头对我很有用(Excel 2007),因为Excel可以识别该文件为UTF-8。否则,保存文件并使用导入向导也可以,但不是最理想的方法。

1
它仍然会打开文本导入向导,所以区别在于你可以直接双击,所以仍然不是理想的解决方案,但目前来说是唯一已知的解决方法。 - haridsv
对我来说,Excel 2007 没有出现导入向导。 - Victor Nicollet
我也没有导入向导 - 如果存在UTF8 BOM /签名(EF BB BF),则它按预期工作。 - Danny Tuppeny
4
不,@AlastairMcCormack,这取决于它的编码方式。"\ufeff" 以 UTF-8 编码时,它确切地是 EF BB BF。(如果以 UTF-16 编码,它只会有两个字节。) - Dave Burt
看看这个。对我也有效。 - jacob spitzer
显示剩余2条评论

34
以下是我在项目中发送Microsoft Excel给用户时使用的PHP代码:
  /**
   * Export an array as downladable Excel CSV
   * @param array   $header
   * @param array   $data
   * @param string  $filename
   */
  function toCSV($header, $data, $filename) {
    $sep  = "\t";
    $eol  = "\n";
    $csv  =  count($header) ? '"'. implode('"'.$sep.'"', $header).'"'.$eol : '';
    foreach($data as $line) {
      $csv .= '"'. implode('"'.$sep.'"', $line).'"'.$eol;
    }
    $encoded_csv = mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');
    header('Content-Description: File Transfer');
    header('Content-Type: application/vnd.ms-excel');
    header('Content-Disposition: attachment; filename="'.$filename.'.csv"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: '. strlen($encoded_csv));
    echo chr(255) . chr(254) . $encoded_csv;
    exit;
  }

更新:文件名改进和错误修复正确的长度计算。感谢TRiG@ivanhoe011


1
我在这个页面上尝试了几个其他建议,但这个方法对我在Excel 2007中有效。最重要的更改是使用制表符而不是逗号(即使它是一个.csv文件),以及在调用mb_convert_encoding()之前回显两个字符的行。我还必须重新编译PHP并使用--enable-mbstring来获取对mb_convert_encoding()的支持。谢谢! - Russell G
1
这对我也很有用,谢谢。但是在Safari中,我的控制台会出现一个错误:“资源被解释为文档,但传输的是...”我猜这是一个WebKit怪癖,根据https://dev59.com/gW865IYBdhLWcg3wR8ah,但也许不是,或者有人已经找到了解决方案。此外,在您的示例中,我建议更改:`'Content-Disposition: attachment; filename="'.$filename.'.csv"'`因为Firefox需要双引号,否则它将在空格后截断您的文件名。 - kasimir
你为什么要输出 CSV (text/csv),但却称其为 Excel (application/vnd.ms-excel)? - TRiG
2
这个很棒!我可以确认它在 Mac 上也能工作(在 Office 2011 中)。 - Jonathan
1
这不应该是 header('Content-Length: '. mb_strlen($encoded_csv, 'UTF-16LE')); 吗? - Rich Bradshaw

15

所有Excel版本(包括2003和2007)和文件类型的答案

这里的大多数其他答案只涉及到他们自己的Excel版本,可能对您并没有实际帮助,因为他们的答案可能不适用于您的Excel版本。

例如,添加BOM字符会导致自动列分隔符识别问题,但并非所有Excel版本都存在此问题。

有三个变量决定是否适用于大多数Excel版本:

  • 编码方式
  • BOM字符的存在
  • 单元格分隔符

SAP的某个执着的人尝试了每种组合并报告了结果。最终的结论是:使用UTF16le格式、带BOM字符并使用制表符作为分隔符可在大多数Excel版本中使用。

您不相信我?我也不会相信,但请看这里并哭泣:http://wiki.sdn.sap.com/wiki/display/ABAP/CSV+tests+of+encoding+and+column+separator


为什么不直接添加sep=,或者你想使用的其他内容呢?如果你已经添加了BOM,我认为你不会反对在文件中添加其他内容。 - Casey
实际上,回答我自己的问题,你不会添加字段分隔符声明,因为它会导致这个技巧停止工作。所以基本上,如果您的用户拥有错误的区域设置,那么这是编码混乱或您的文件未被正确解释为CSV。 - Casey
2
utf-16le + BOM(0xFF 0xFE) + tab 是最好的。 - zhaozhi
啊,这个建议解释了为什么 Pandas 无法打开我提供的“Excel”文件,直到我手动确定它是 utf-16-le 编码和制表符分隔的。 - David Waterworth

10

在输出CSV数据之前,先输出UTF-8 BOM。这可以解决Windows中的所有字符问题,但对于Mac无效。

echo "\xEF\xBB\xBF";

这对我有效,因为我需要生成一个仅在 Windows PC 上使用的文件。


并非适用于每种列分隔符或每个Excel版本。请查看我的答案(目前在下方)。 - Christiaan Westerbeek
我应该把这个放在哪里? :-) - Denny
它也适用于当前的Mac。 - Wieczo

10

导入时选择UTF-8编码。如果您使用的是Office 2007,则可以在打开文件后选择: 就在这个地方。


1
这很有用。我修改了问题,询问如何在不使用向导的情况下完成此操作。 - Freddo411

7

在没有任何服务包的情况下,无论是否使用BOM(U+ffef或0xEF, 0xBB, 0xBF),UTF-8在办公室2007中都无法正常工作。 安装sp3可以使UTF-8在0xEF,0xBB,0xBF BOM前缀的情况下正常工作。

使用“utf-16-le”进行编码,以及在分隔符中使用制表符时,UTF-16可以通过python工作, 必须手动编写BOM,然后使用“utf-16-le”而不是“utf-16”,否则每个编码(encode())都会将BOM添加到每一行写出的文本中, 这将出现在第二行和之后的第一列中的垃圾数据。

无法确定UTF-16是否在未安装任何服务包的情况下正常工作,因为现在无法回退。 叹气

这是在Windows上的情况,不知道MAC上的office怎么样。

对于两种工作情形,在直接从浏览器启动下载并且文本导入向导未进行干预的情况下,导入操作效果良好,与您期望的一样。


也适用于 Excel 2011 for Mac。 - Adam
感谢您的帖子,即使您没有安装Office 2007 SP3,使用UTF-16LE也是可以的,但BOM应该是0xFF 0xFE。谢谢。 - zhaozhi

4
作为 Fregal 所说,\uFEFF 是正确的方式。
<%@LANGUAGE="JAVASCRIPT" CODEPAGE="65001"%>
<%
Response.Clear();
Response.ContentType = "text/csv";
Response.Charset = "utf-8";
Response.AddHeader("Content-Disposition", "attachment; filename=excelTest.csv");
Response.Write("\uFEFF");
// csv text here
%>

1
只需看看并了解在使用BOM时Excel 2007如何忽略您的制表符分隔符。您必须想出更多的解决方案。 - Christiaan Westerbeek

3

您可以将html文件保存为xls扩展名,并且重音符号将正常工作(至少在2007年之前的版本中)。

示例:请将此文件另存为test.xls (使用Notepad中的“另存为UTF-8”):

<html>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8" />
<table>
<tr>
  <th>id</th>
  <th>name</th>
</tr>
<tr>
 <td>4</td>
 <td>Hélène</td>
</tr>
</table>
</html>

有一个有趣的选项。它可以打开文本,但由于某种原因,整个页面都是完全白色的,没有经典的电子表格行和列的分隔线(适用于Mac的Office)。 - Sebastian Sastre
是的,在Windows上的Office 2007中也是同样的情况。老实说,它能够工作始终让我感到惊讶。请注意,如果您将 border="1" 添加到表格中,您会得到线条,但只围绕着4个单元格 :) - Benjol

3

我也注意到这个问题在一段时间前已经被“回答”了,但我不理解为什么有人说你不能在Excel中成功打开一个UTF8编码的CSV文件而不使用文本向导。

我的可重复体验:

在记事本中键入Old MacDonald had a farm,ÈÌÉÍØ,然后按Enter键,然后选择“另存为”(使用UTF-8选项)。

使用Python来展示里面实际上是什么:

>>> open('oldmac.csv', 'rb').read()
'\xef\xbb\xbfOld MacDonald had a farm,\xc3\x88\xc3\x8c\xc3\x89\xc3\x8d\xc3\x98\r\n'
>>> ^Z

很好,记事本已经在文件开头放置了BOM。

现在进入Windows资源管理器,双击文件名或右键使用“打开方式...”,Excel(2003)会如预期地显示出来。


@Cocowalla:好吧,我刚刚尝试了一下(再次尝试;在发布之前我已经测试过了),它可以在Excel 2007中使用(这是我现在正在使用的版本)。你是否执行了open('oldmac.csv', 'rb').read()来验证你的输入? - John Machin
我没有尝试过Excel 2007(我知道Excel 2007可以很好地读取带BOM的UTF-8文件),我是用Excel 2003尝试的。 - Cocowalla
@Cocowalla:我在使用Excel 2003时,它对我很有效。您确定您的Excel 2003已安装最新的服务包吗?您是否按照我的建议验证了输入? - John Machin
我确实核实了记事本在文件开头插入了BOM,但我使用的是Excel 2003 SP2(SP3可用)-所以我猜这只适用于SP3。 - Cocowalla

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