Microsoft Excel 破坏 .csv 文件中的变音符号?

Posted

技术标签:

【中文标题】Microsoft Excel 破坏 .csv 文件中的变音符号?【英文标题】:Microsoft Excel mangles Diacritics in .csv files? 【发布时间】:2010-09-14 09:10:03 【问题描述】:

我正在以编程方式将数据(使用 php 5.2)导出到 .csv 测试文件中。 示例数据:Numéro 1(注意带重音的 e)。 数据为 utf-8(无前置 BOM)。

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

我可以在正确显示它的文本编辑器 (UltraEdit) 中打开它。 UE 报告字符为decimal 233

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

【问题讨论】:

我很想了解更多关于您的 BOM 解决方案的信息,因为我相信我已经尝试过对我不起作用的“EF BB BF”。 选择的工作解决方案是: * 包括 BOM ; utf-8 * 使用此标头:'内容类型:文本/纯文本; charset=utf-8' 这在 excel 2003 和 excel 2007 中“有效”——有效 = 在没有导入向导的情况下打开并正确呈现变音符号。我没有验证是否需要 BOM。 BOM是必填项,我刚刚测试了这个。没有它,特殊字符将无法正常显示。 如果有人能详细说明如何添加 BOM(字节顺序标记),我会很高兴的。如果我只是执行类似 Response.Write(EF BB BF") 之类的操作,那么这些字符只会显示在文件的开头。 sydneyos:正如 Fergal 在下面所说的那样;将 \uFEFF 添加到您的字符串中。 【参考方案1】:

这只是字符编码的问题。看起来您正在将数据导出为 UTF-8:UTF-8 中的 é 是两字节序列 0xC3 0xA9,在 Windows-1252 中解释为 é。当您将数据导入 Excel 时,请务必告诉它您使用的字符编码是 UTF-8。

【讨论】:

我已确认数据为 UTF-8。我在文件中输入什么让 excel 知道我的数据是 utf-8(BOM?) 我认为需要更改文件编码,excel使用系统默认codepage处理csv文件 我不完全确定,因为我当前使用的机器上没有安装 Excel,但是使用 OpenOffice,当您导入 CSV 文件时,有一个字符编码下拉框。从那里,选择 Unicode (UTF-8)。 Excel 没有 AFAIK 下拉菜单 查看this。对我也有用。【参考方案2】:

检查您生成文件的编码,要使excel正确显示文件,您必须使用系统默认代码页。

您使用哪种语言?如果是 .Net,您只需要在生成文件时使用 Encoding.Default。

【讨论】:

导出数据为utf-8。我正在用 php 5 编写导出文件 将数据转码到 Windows-1252 代码页,我不知道如何用 php 完成它【参考方案3】:

CSV 格式在 Excel 中实现为 ASCII,而不是 unicode,从而破坏了变音符号。我们遇到了同样的问题,这就是我发现官方 CSV 标准在 Excel 中被定义为基于 ASCII 的方式。

【讨论】:

其实 CSV 并没有绑定特定的编码。假设 ASCII 是 Excel。 en.wikipedia.org/wiki/Comma-separated_values 我就是这么说的。 “在 Excel 中实现为 ASCII”、“在 Excel 中定义为基于 ASCII 的 CSV”。不知道你在说什么,因为你似乎同意我的观点。 其实你说“CSV 格式被实现为 ASCI”,我想这就是混乱的根源。【参考方案4】:

格式正确的 UTF8 文件可以将 Byte Order Mark 作为其前三个八位字节。这些是十六进制值 0xEF、0xBB、0xBF。这些八位位组用于将文件标记为 UTF8(因为它们与“字节顺序”信息无关)。1 如果此 BOM 不存在,则让消费者/读者推断文本的编码类型。不支持 UTF8 的读取器会将字节读取为其他编码,例如 Windows-1252,并在文件开头显示字符 

存在一个已知错误,即 Excel 在通过文件关联打开 UTF8 CSV 文件时假定它们采用单字节编码,忽略 UTF8 BOM 的存在。任何系统默认代码页或语言设置都无法解决此问题。 BOM 不会在 Excel 中提供线索 - 它只是行不通。 (少数报告声称 BOM 有时会触发“导入文本”向导。)此错误似乎存在于 Excel 2003 和更早版本中。大多数报告(在此处的答案中)说这已在 Excel 2007 及更高版本中得到修复。

请注意,您可以始终*使用“导入文本”向导在 Excel 中正确打开 UTF8 CSV 文件,该向导允许您指定正在打开的文件的编码。当然这样就不太方便了。

此答案的读者很可能处于他们不特别支持 Excel à 和其他类似的 Windows- 1252 个字符。 添加 UTF8 BOM 可能是您最好、最快的解决方法。

如果您在使用旧版 Excel 时遇到用户问题,并且 Excel 是 CSV 的唯一使用者,您可以通过导出 UTF16 而不是 UTF8 来解决此问题。 Excel 2000 和 2003 将正确双击打开这些。 (其他一些文本编辑器可能会遇到 UTF16 问题,因此您可能需要仔细权衡您的选择。)


* 除非你不能,(至少)Excel 2011 for Mac 的导入向导实际上并不总是适用于所有编码,不管你告诉它什么。 轶事证据> :)

【讨论】:

花了我很长时间才找到指定编码的位置。保存对话框 > 工具按钮 > Web 选项 > 编码选项卡。他们确实很擅长隐藏这些重要的事情。 错误:将 BOM 添加到 UTF-8 文件可以正确加载该文件,而不需要 Excel 2007 中的导入向导。 我们发现了和 Victor 今天说的一样的东西(使用 Excel 2010,这是我们可用的全部)。添加 UTF-8 BOM/签名 (EF BB BF) 似乎修复了使用系统默认编码的双击问题,并正确使用了 UTF8 :) 一般而言,UTF-8 编码的文件应该在前面加上字节顺序标记。 UTF-8 没有可变字节顺序,将其放在那里会破坏 UTF-8 的 ASCII 兼容性。有一些特定的文件格式允许或鼓励使用 UTF-8 仿 BOM,否则应避免使用。 CSV 完全不了解编码,因此任何人都可以猜测给定工具是否会将字节序列 0xEF 0xBB 0xBF 解释为 UTF-8 的指示符;第一个单元格中的不可见控制字符;第一个单元格中的字符;或完全不同的东西。 @Ian:没有人确切知道它是 UTF-8 with BOM - 0xEF 0xBB 0xBF 在大多数遗留编码中也是一个有效序列(因此它经常被误解为ISO-8859-1 或 cp1252 并显示为 )。它仅有助于猜测算法,以及专门考虑它的文件格式(例如 XML)。在 UTF-8 文件中包含虚假 BOM 的缺点是您破坏了它们的 ASCII 兼容性(UTF-8 的一个主要卖点)许多不了解编码的文本工具会在面对意想不到的领先虚假 BOM 时崩溃。【参考方案5】:

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

【讨论】:

这很有用。我已修改问题以询问如何在不求助于向导的情况下执行此操作【参考方案6】:

预置 BOM (\uFEFF) 对我有用 (Excel 2007),因为 Excel 将文件识别为 UTF-8。否则,保存它并使用导入向导可以,但不太理想。

【讨论】:

它仍然会打开文本导入向导,所以不同之处在于您可以简单地双击,所以仍然不理想,但无论如何都是唯一已知的解决方案。 对我来说,Excel 2007 不会出现导入向导。 我也没有导入向导 - 如果存在 UTF8 BOM/签名 (EF BB BF),它将按预期工作。 另外,\ufeffUTF-16 (BE) BOM 而不是 UTF-8 BOM 不,@AlastairMcCormack,两者都是,取决于它的编码方式。编码为 UTF-8 的 "\ufeff" 正好是 EF BB BF。 (编码为 UTF-16 将只有两个字节。)【参考方案7】:

正如 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
%>

【讨论】:

只需观察并了解使用 BOM 时 Excel 2007 中如何忽略制表符分隔符。你必须想出更多的东西。【参考方案8】:

您可以保存扩展名为“xls”的 html 文件,并且重音符号可以使用(至少 2007 年之前)。

示例:将此(在记事本中使用另存为 utf8)另存为 test.xls:

<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>

【讨论】:

有趣的选项。它会正确打开文本,但由于某种原因,所有页面都是完全白色的。没有分隔行和列的经典电子表格行(Office for mac) 是的,在 Windows 上的 Office 2007 中也是如此。老实说,它总是让我感到惊讶。 (注意,如果您将border="1" 添加到表格中,您确实会得到行,但只是在 4 个单元格周围:)【参考方案9】:

我还注意到,这个问题前段时间已经“回答”了,但我不明白那些说如果不使用文本向导就无法在 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() 验证您的输入? 我没有尝试使用 Excel 2007(我知道 Excel 2007 可以很好地读取带有 BOM 的 UTF-8 文件),我尝试使用 Excel 2003 @Cocowalla:当我拥有 Excel 2003 时,它对我有用。您确定您拥有最新的 Excel 2003 服务包吗?您是否按照我的建议验证了您的输入? 我确实验证了记事本在文件开头粘贴了一个 BOM,但我使用的是 Excel 2003 SP2(SP3 可用) - 所以我猜这仅适用于 SP3【参考方案10】:

以下是我在项目中向用户发送 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

【讨论】:

我在此页面上尝试了其他几个建议,但这在 Excel 2007 中对我有用。最重要的更改是使用制表符而不是逗号(即使它是 .csv 文件)和上面的行这会回显两个字符,然后调用 mb_convert_encoding()。我还必须使用 --enable-mbstring 重新编译 PHP 以获得对 mb_convert_encoding() 的支持。谢谢! 这对我也很有效,谢谢。但是,在 Safari 中,我的控制台中出现错误“资源解释为文档但传输为...”我猜这是 WebKit 的怪癖,判断 ***.com/questions/3899426/…,但也许不是和/或有人找到了解决方案。此外,在您的示例中,我建议进行更改:'Content-Disposition: attachment; filename="'.$filename.'.csv"' 因为 Firefox 需要双引号,否则它将在空格后切断您的文件名。 你为什么输出 CSV (text/csv) 却称它为 Excel (application/vnd.ms-excel)? 这很好用!我可以确认它也可以在 Mac 上运行(在 Office 2011 中)。 不应该是header('Content-Length: '. mb_strlen($encoded_csv, 'UTF-16LE'));吗?【参考方案11】:

Excel 2007 可以正确读取带有 BOM (EF BB BF) 编码的 csv 的 UTF-8。

Excel 2003(可能更早版本)使用 BOM (FF FE) 读取 UTF-16LE,但使用制表符而不是逗号或分号。

【讨论】:

【参考方案12】:

我只能让 CSV 在 Excel 2007 中正确解析为制表符分隔的 little-endian UTF-16,从正确的字节顺序标记开始。

【讨论】:

【参考方案13】:

在输出 CSV 数据之前回显 UTF-8 BOM。这修复了 Windows 中的所有字符问题,但不适用于 Mac。

echo "\xEF\xBB\xBF";

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

【讨论】:

并非所有类型的列分隔符或每个 Excel 版本都如此。请阅读下面的答案(暂时如下)。【参考方案14】:

在 Django 中将 BOM 写入输出 CSV 文件实际上对我有用:

def handlePersoonListExport(request):
    # Retrieve a query_set
    ...

    template = loader.get_template("export.csv")
    context = Context(
        'data': query_set,
    )

    response = HttpResponse()
    response['Content-Disposition'] = 'attachment; filename=export.csv'
    response['Content-Type'] = 'text/csv; charset=utf-8'
    response.write("\xEF\xBB\xBF")
    response.write(template.render(context))

    return response

更多信息http://crashcoursing.blogspot.com/2011/05/exporting-csv-with-special-characters.html谢谢大家!

【讨论】:

是的,这适用于 Excel 2010。在 Java 中使用 printWriter.print('\ufeff'),另请参阅 How to add a UTF-8 BOM in java。 查看this。对我也有用。【参考方案15】:

在 Ruby 1.8.7 中,我将每个字段编码为 UTF-16 并丢弃 BOM(可能)。

以下代码是从active_scaffold_export中提取的:

<%                                                                                                                                                                                                                                                                                                                           
      require 'fastercsv'                                                                                                                                                                                                                                                                                                        
      fcsv_options =                                                                                                                                                                                                                                                                                                            
        :row_sep => "\n",                                                                                                                                                                                                                                                                                                        
        :col_sep => params[:delimiter],                                                                                                                                                                                                                                                                                          
        :force_quotes => @export_config.force_quotes,                                                                                                                                                                                                                                                                            
        :headers => @export_columns.collect  |column| format_export_column_header_name(column)                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                                                

      data = FasterCSV.generate(fcsv_options) do |csv|                                                                                                                                                                                                                                                                           
        csv << fcsv_options[:headers] unless params[:skip_header] == 'true'                                                                                                                                                                                                                                                      
        @records.each do |record|                                                                                                                                                                                                                                                                                                
          csv << @export_columns.collect  |column|                                                                                                                                                                                                                                                                              
            # Convert to UTF-16 discarding the BOM, required for Excel (> 2003 ?)                                                                                                                                                                                                                                     
            Iconv.conv('UTF-16', 'UTF-8', get_export_column_value(record, column))[2..-1]                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                
        end                                                                                                                                                                                                                                                                                                                      
      end                                                                                                                                                                                                                                                                                                                        
    -%><%= data -%>

重要的一行是:

Iconv.conv('UTF-16', 'UTF-8', get_export_column_value(record, column))[2..-1]

【讨论】:

【参考方案16】:

我发现的另一个解决方案是将结果编码为 Windows 代码页 1252(Windows-1252 或 CP1252)。这可以通过例如将Content-Type 适当地设置为text/csv; charset=Windows-1252 并类似地设置响应流的字符编码来完成。

【讨论】:

感谢这个。适用于 excel windows 和 mac。我正在使用它。 这仅在您的非 ascii 字符范围完全在 Windows-1252 范围内时才有效。因此,例如,没有韩文/中文/日文,没有西里尔文等。但我想你会在大多数西欧语言中使用这个。【参考方案17】:

我找到了解决问题的方法。这是一个令人讨厌的 hack,但它确实有效:使用 Open Office 打开文档,然后将其保存为任何 excel 格式;生成的 .xls.xlsx 将显示重音字符。

【讨论】:

OP 说他正在以编程方式导出,因此他不是在寻找需要手动干预的解决方案。【参考方案18】:

如果您像我一样在 vb.net 中有旧代码,则以下代码对我有用:

    Response.Clear()
    Response.ClearHeaders()
    Response.ContentType = "text/csv"
    Response.Expires = 0
    Response.AddHeader("Content-Disposition", "attachment; filename=export.csv;")
    Using sw As StreamWriter = New StreamWriter(Context.Response.OutputStream, System.Text.Encoding.Unicode)
        sw.Write(csv)
        sw.Close()
    End Using
    Response.End()

【讨论】:

【参考方案19】:

UTF-8 在没有任何服务包的 Office 2007 中对我不起作用,无论是否有 BOM (U+ffef 或 0xEF,0xBB,0xBF 都不起作用) 当 0xEF,0xBB,0xBF BOM 被前置时,安装 sp3 会使 UTF-8 工作。

UTF-16 在 python 中使用带有 0xff 0xef 的“utf-16-le”编码时有效 BOM 前置,并使用制表符作为分隔符。 我不得不手动写出 BOM,然后使用“utf-16-le”而不是“utf-16”, 否则每个 encode() 将 BOM 附加到写出的每一行 在第二行的第一列及之后显示为垃圾。

无法判断 UTF-16 是否可以在没有安装任何 sp 的情况下工作,因为 我现在不能回去了。 叹息

这是在 windows 上的,不知道 MAC 的 office。

对于这两种工作情况,直接从 浏览器和文本导入向导不会干预,它会像您期望的那样工作。

【讨论】:

也适用于 Excel 2011 for Mac。 感谢您的帖子,即使您没有安装office 2007 sp3,也可​​以使用utf-16le,但BOM应该是0xFF 0xFE【参考方案20】:

请注意,包含 UTF-8 BOM 不一定是个好主意 - Mac 版本的 Excel 会忽略它,实际上会将 BOM 显示为 ASCII……电子表格第一个字段开头的三个讨厌的字符……

【讨论】:

我知道这条评论是 6 年后的事,但 FWIW:使用 JavaScript 下载像 '\uFEFF' + myCsvString 这样的文件在 Mac Excel 15.19.1 (2016) 上可以正常工作。【参考方案21】:

用notepad++打开文件csv 点击编码,选择转换为 UTF-8(不转换为 UTF-8(无 BOM)) 保存 用excel双击打开 希望有所帮助 克里斯托夫·格里森

【讨论】:

这不能回答问题,因为它应该以编程方式完成,并且不需要用户干预来手动重新保存每个文件【参考方案22】:

Excel 版本 (2003 + 2007) 和文件类型的所有组合的答案

这里的大多数其他答案仅涉及他们的 Excel 版本,不一定对您有帮助,因为他们的答案可能不适用于您的 Excel 版本。

例如,添加 BOM 字符会导致自动列分隔符识别出现问题,但并非每个 Excel 版本都会出现问题。

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

编码 BOM 角色存在 单元格分隔符

SAP 的一位坚忍不拔的人尝试了所有组合并报告了结果。最终结果?使用 UTF16le 与 BOM 和制表符作为分隔符,使其在大多数 Excel 版本中工作。

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

【讨论】:

为什么不直接添加sep=, 或您想使用的任何内容?如果您已经添加了 BOM,我假设您不反对向文件中添加内容。 好吧,实际上,要回答我自己的问题,您不会添加字段分隔符声明,因为它会导致此技巧停止工作。所以基本上它是乱码编码或者如果您的用户有错误的区域设置,您的文件没有被正确解释为 CSV。 utf-16le + BOM(0xFF 0xFE) + tab 是最好的

以上是关于Microsoft Excel 破坏 .csv 文件中的变音符号?的主要内容,如果未能解决你的问题,请参考以下文章

python从Microsoft Excel文件中导入数据

是否可以通过 PHP 代码将 excel csv 文件直接打开到 microsoft excel 或其他 excel 支持的应用程序中

如何用C#将Excel转换成XML文件,希望把一些关键的地方讲得详细些,能让我明白就给分

excel打开csv 出现乱码怎么解决

csv文件转换成excel的方法

从 Excel / CSV 批量插入到 SQL Server