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


190

我正在以编程方式将数据(使用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”。
詹姆斯·贝克

3
选择的可行解决方案是:*包含BOM;utf-8 *使用以下标头:'Content-type:text / plain; charset = utf-8'在excel 2003和excel 2007中“工作”-在没有导入向导的情况下打开了工作=并正确显示了变音符号。我没有验证是否需要BOM。
Freddo411

2
BOM是必需的,我现在对此进行了测试。没有它,特殊字符将无法正常显示。
Alex Ciminian 2010年

2
如果有人可以说更多有关如何添加BOM(字节顺序标记)的信息,我将不胜感激。如果我只是做类似的Response.Write(EF BB BF“)这些字符只是显示在文件的开头。
sydneyos

悉尼:正如Fergal在下面说的;在\ uFEFF前面加上字符串。
noocyte 2011年

Answers:


242

格式正确的UTF8文件可以将字节顺序标记作为其前三个八位字节。这些是十六进制值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 <2007的情况下,而是将原始UTF8文本发送到Excel,这会误解它,并在文本中加上Ã其他类似的Windows-1252字符。 添加UTF8 BOM表可能是最好,最快的解决方案。

如果您对使用旧版Excel的用户不满意,而Excel是CSV的唯一使用者,则可以通过导出UTF16而不是UTF8来解决此问题。Excel 2000和2003将正确双击这些文件。(其他一些文本编辑器可能会对UTF16产生问题,因此您可能必须仔细权衡一下您的选择。)


*除非您不能这样做,否则(至少)Excel 2011 for Mac的“导入向导”实际上并不总是适用于所有编码,无论您说什么。</ anecdotal-evidence> :)


14
永远让我找到在哪里指定编码。保存对话框>工具按钮> Web选项>编码选项卡。他们肯定擅长隐藏如此重要的事情。
Triynko 2010年

6
错误:添加一个BOM到UTF-8文件加载该文件正确,而不需要在Excel 2007中导入向导
维克多尼科莱特

3
我们发现了和Victor今天说的一样的事情(使用Excel 2010,这就是我们所拥有的全部)。添加UTF-8 BOM /签名(EF BB BF)似乎可以解决使用系统默认编码的双击问题,并正确使用了UTF8 :)
Danny Tuppeny 2011年

20
一般来说,一个UTF-8编码的文件应该不会有一个字节顺序标记前缀。UTF-8没有可变的字节顺序,将其放置在那里会破坏UTF-8的ASCII兼容性。有一些特定的文件格式允许或鼓励使用UTF-8人造BOM,但应避免使用。CSV完全无视编码,因此任何人都可以猜测给定的工具是否会将字节序列0xEF 0xBB 0xBF解释为UTF-8的指示符。第一个单元格中不可见的控制字符;第一个单元格中的字符;或完全其他的东西。
bobince 2012年

3
@Ian:没人可以肯定它是带有 BOM 的UTF-8 还是0xEF 0xBB 0xBF在大多数传统编码中也是有效的序列(因此它经常被误解为ISO-8859-1或cp1252并显示为)。它仅有助于猜测算法,以及有助于专门考虑算法的文件格式(例如XML)。在UTF-8文件中包含仿造BOM的缺点是,您破坏了它们的ASCII兼容性(UTF-8的主要卖点)。许多对编码无知的文本工具都会遇到意想不到的领先仿造BOM。
bobince 2012年

39

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


1
它仍然会打开文本导入向导,因此区别在于您只需双击即可,因此仍然不是理想的方法,但仍然是唯一已知的解决方案。
haridsv 2010年

对我来说,没有导入向导会使用Excel 2007
维克多尼科莱特

我也没有导入向导-如​​果存在UTF8 BOM /签名(EF BB BF),它可以按预期工作。
Danny Tuppeny 2011年

另外,\ufeffUTF-16(BE)BOM而不是UTF-8 BOM
Alastair McCormack

2
不,@ AlastairMcCormack,取决于编码方式。编码为UTF-8的“ \ ufeff”正好是EF BB BF。(编码为UTF-16,它将只是两个字节。)
Dave Burt

30

以下是将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()。我还必须使用--enable-mbstring重新编译PHP,以获得对mb_convert_encoding()的支持。谢谢!
罗素G

1
谢谢,这对我也很好。但是,在Safari中,我的控制台出现错误:“资源被解释为文档,但传输为...”,我认为这是WebKit的怪癖,判断stackoverflow.com/questions/3899426 / ...,但可能不是,并且/或者有人拥有找到了解决方案。此外,在您的示例中,我建议您进行更改:'Content-Disposition: attachment; filename="'.$filename.'.csv"'因为Firefox需要双引号,否则它将在空格后切断文件名。
kasimir 2012年

为什么要输出CSV(text/csv)但将其称为Excel(application/vnd.ms-excel)?
TRiG 2013年

2
这很棒!我可以确认它也可以在Mac上运行(在Office 2011中)。
乔纳森

这不是header('Content-Length: '. mb_strlen($encoded_csv, 'UTF-16LE'));吗?
Rich Bradshaw

13

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

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

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

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

  • 编码方式
  • BOM字符存在
  • 细胞分离器

SAP的坚忍者尝试了每种组合并报告了结果。最终结果?使用带有BOM表和制表符的UTF16le作为分隔符,可以在大多数Excel版本中使用它。

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


为什么不添加sep=,或使用任何您想使用的东西呢?如果您已经添加了BOM表,那么我想您不反对在文件中添加内容。
凯西2015年

好吧,实际上,要回答我自己的问题,您不会添加字段分隔符声明,因为它导致此技巧停止工作。因此,基本上是乱码编码,如果用户的区域设置错误,则文件无法正确解释为CSV。
凯西2015年

1
UTF-16LE + BOM(0xFF的0xFE的)+标签是最好的
兆致

10

导入时选择UTF-8编码。如果您使用Office 2007,则在此处选择它:打开文件后。


1
这很有用。我已经修改了问题,询问如何在不借助向导的情况下执行此操作
Freddo411

9

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

echo "\xEF\xBB\xBF";

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


对于每种类型的列分隔符或每种Excel版本,均不正确。请在下面阅读我的答案(现在下面)。
Christiaan Westerbeek 2014年

7

UTF-8在Office 2007中在没有任何Service Pack,带或不带BOM(U + ffef或0xEF,0xBB,0xBF均不起作用)的情况下不适用于我,当0xEF,0xBB,0xBF BOM为0时,安装sp3可使UTF-8工作前置。

当使用“ utf-16-le”(带有0xff 0xef BOM前缀)并使用制表符作为分隔符在python中进行编码时,UTF-16可以工作。我必须手动写出BOM表,然后使用“ utf-16-le”而不是“ utf-16”,否则,每个encode()都会在写出的每一行之前添加BOM表,在行的第一列上显示为垃圾第二行及之后。

因为我现在不能返回,所以无法确定UTF-16是否可以在没有安装任何sp的情况下工作。

这是在Windows上,关于MAC Office的问题。

对于这两种情况,导入都可以在直接从浏览器启动下载时进行,并且文本导入向导不会相互干扰,就像您期望的那样。


也适用于Mac的Excel 2011。
亚当,

感谢您的发帖,即使您没有安装Office 2007 sp3,也可​​以使用utf-16le,但是BOM应该为0xFF 0xFE
zhaozhi 2015年

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中如何忽略制表符分隔符。您还必须提出更多建议。
克里斯蒂安·韦斯特贝克

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()验证您的输入?
约翰·马钦

我没有尝试使用Excel 2007(我知道Excel 2007可以读取带有BOM的UTF-8文件就可以了),我尝试使用Excel 2003
Cocowalla 2010年

@Cocowalla:嗯,当我拥有Excel 2003时,它对我有用。您确定您具有适用于Excel 2003的最新Service Pack吗?您是否按照我的建议验证了您的输入?
约翰·马钦

我确实确认记事本在文件的开头粘贴了BOM,但是我使用的是Excel 2003 SP2(可以使用SP3)-所以我想这只能在SP3中使用
Cocowalla 2010年

2

您可以保存带有扩展名'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>

有趣的选择。它会正确打开文本,但由于某种原因,所有页面都是全白的。没有经典的电子表格行来分隔行和列(Mac办公)
Sebastian Sastre

是的,在Windows 2007的Office 2007中也是如此。老实说,它的确奏效令我感到惊讶。(请注意,如果您将border="1"表格添加到表格中,则确实会获得行,但仅在4个单元格周围:)
Benjol 2011年

1

这只是字符编码的问题。似乎您将数据导出为UTF-8:UTF-8中的é是两个字节的序列0xC3 0xA9,在Windows-1252中解释为é。将数据导入Excel时,请确保告诉您所使用的字符编码为UTF-8。


我已经确认数据是UTF-8。我要在文件中放入哪些内容以使excel知道我的数据是utf-8(BOM?)
Freddo411

我认为您需要更改文件编码,excel使用系统默认代码页来处理csv文件
albertein

我不确定,因为我当前使用的计算机上没有安装Excel,但是对于OpenOffice,导入CSV文件时会有一个用于字符编码的下拉框。从那里选择Unicode(UTF-8)。
亚当·罗森菲尔德

Excel没有下拉AFAIK
albertein

1

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


实际上,CSV没有绑定到特定的编码。Excel假设使用ASCII。 en.wikipedia.org/wiki/Comma-separated_values
spoulson

这就是我所说的。“在Excel中实现为ASCII”,“在Excel中定义为基于ASCII的CSV”。当您似乎同意我的观点时,不确定要说的是什么。
杰夫·耶茨(

2
实际上,您说“ CSV格式被实现为ASCI”,我认为这是造成混淆的原因。
RichardOD

1

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

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



1

在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'),另请参阅如何在java中添加UTF-8 BOM
tsauerwein 2011年

1

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


谢谢你这个 适用于Excel Windows和Mac。我在用
塞巴斯蒂安·萨斯特

仅当您的非ASCII字符范围完全在Windows-1252内时,这才起作用。因此,例如,没有韩文/中文/日文,西里尔文等。但是我想您会在大多数西欧语言中顺其自然。
Tom McClure 2012年

1

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


我知道这条评论已经过了6年,但FWIW:使用JavaScript下载类似'\uFEFF' + myCsvStringMac Excel 15.19.1(2016)上的作品的文件。
bobjones

0

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

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


导出数据为utf-8。我正在用php 5编写导出文件
Freddo411

将数据转码为Windows-1252代码页,我不确定如何用php完成它
-albertein

0

如果像我一样在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()

0

我找到了解决问题的方法。这是一个令人讨厌的骇客,但是它可以起作用:使用Open Office打开文档,然后将其保存为任何excel格式;结果.xls.xlsx将显示突出的字符。


1
OP说他正在以编程方式导出,因此他不是在寻找需要手动干预的解决方案。
克里斯蒂安·韦斯特贝克

0

使用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]

-2

在Encode上使用notepad ++ clic打开文件csv,选择“转换为UTF-8(不转换为UTF-8(无BOM))”并使用excel保存为double clic打开,希望对Christophe GRISON有帮助


1
这不能回答问题,因为它应该以编程方式完成,并且不需要用户干预即可手动重新保存每个文件
Joe W
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.