sep=";"语句在由XSL生成的CSV文件中破坏了utf8 BOM。

43

我正在使用XSLT开发CSV导出功能,并且CSV文件将在我的情况下99%与Excel一起使用,因此我必须考虑Excel的行为。

我的第一个问题是CSV中的德语特殊字符。尽管CSV编码为UTF8,但Excel无法正确打开带有UTF8的CSV文件。特殊字符变成了奇怪的符号。我找到了解决这个问题的方法。我只需在内容字节开始处添加3个额外的字节(EF BB BF - 也称为BOM头)。因为UTF8 BOM可以告诉Excel“嘿,老兄,这是UTF8,请正确打开它”。问题解决了!

我的第二个问题是关于分隔符。默认分隔符可能是逗号或分号,取决于地区。我认为在德国是分号,在英国是逗号。为了避免这个问题,我不得不添加以下行:

<xsl:text>sep=;</xsl:text>
或者
<xsl:text>sep=,</xsl:text>

(此分隔符未作为硬编码实现)

我的问题是,如果您在生成带有UT8-BOM的CSV文件时在文件开头添加“sep =;”或“sep =,”,则BOM不再有助于正确显示特殊字符!我确信BOM字节始终位于字节数组的开头。这张屏幕截图来自Mac OS X中的MS Excel:

enter image description here

前3个符号属于BOM标头。

您是否曾经遇到过这样的问题,或者您有任何建议?谢谢。

编辑:

我分享了打印屏幕。

a. 带BOM和 <xsl:text>sep=;</xsl:text>

enter image description here

b. 仅带BOM

enter image description here

Java代码:

// Write the bytes
ServletOutputStream out = resp.getOutputStream();
if(contentType.toString().equals("CSV")) {
  // The additional bytes in below is prefix indicates that the content is in UTF-8.
  out.write(239);
  out.write(187);
  out.write(191);
} 
out.write(bytes); // Content bytes, in this case XSL

XSL 代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes" />

    <xsl:template match="/">
    <xsl:text>sep=;</xsl:text>
    <table>
        ...
        </table>
</xsl:template>

2
你说的 BOM 中断 是什么意思?你是指在文件的开头写出字符序列 sep=; 而不是 BOM 头吗? - Marcus Rickert
如果您展示输入、XSLT代码以及预期/实际输出,那么您得到良好答案的机会会大大提高。 - Mathias Müller
1
许多地区版本的 Excel 无法处理 CSV 中的 Unicode。当遇到这种困难时,一个简单的解决方案是将文件名扩展名从 .csv 更改为 .txt;然后使用“打开”命令从已经运行的 Excel 中打开文件。您还可以考虑生成一种 SpreadsheetML 文件类型。 - michael.hor257k
很遗憾,这超出了范围。它必须只有CSV格式。 - Adem İlhan
1
那么,你就需要在某些方面做出妥协。顺便说一句,我认为你不需要宣布分隔符 - Excel 应该足够聪明,可以自己找到它。更不用说这不符合 CSV “标准”(如果有的话)。我不知道另外 1% 是什么,但我相信很多应用程序会因此出错。 - michael.hor257k
显示剩余3条评论
3个回答

14

你说得对,在Excel 2007中,当用户双击一个CSV文件时,无法正确加载编码和分隔符。

似乎在BOM之后指定sep=会使它忘记BOM已经告诉它是UTF-8编码。

你必须指定BOM,因为在某些地区,Excel无法检测到分隔符。例如,在丹麦语中,默认的分隔符是;。如果输出制表符或逗号分隔的文本,则无法检测到分隔符;而在其他地区,如果使用分号分隔,也无法加载。你可以通过更改Windows设置中的区域格式来测试这一点 - Excel会相应地进行调整。

根据这个问题: 是否有办法让Excel自动识别UTF-8的CSV文件?

从答案来看,似乎唯一的方法是使用带BOM的UTF-16 LE编码

请注意,根据http://wiki.scn.sap.com/wiki/display/ABAP/CSV+tests+of+encoding+and+column+separator?original_fqdn=wiki.sdn.sap.com的说法,如果您使用utf16-le和制表符分隔符,则可以正常工作。
我一直在想Excel是否会读取sep=;,然后重新调用方法获取CSV文本并丢失BOM - 我尝试了给出不正确的文本,但找不到任何解决方法告诉Excel同时采用sep和编码。

微软支持对此有何说法?他们对这个问题的解决方案是什么? - dawciobiel

11

这是我使用Excel 2013测试的结果。

如果您只能使用UTF-8编码,有一个解决方法,即BOM + 数据 + sep=;

输入(使用UTF8编写)

\ufeffSome;Header;Columns
Wîth;Fàncÿ;Stûff
sep=;

输出

|Some|Header|Columns|
|Wîth|Fàncÿ |Stûff  |
|sep=|      |       |

问题在于,虽然Excel可以正确解释sep=;,但它会在最后一行的第一列中显示sep=(是的,它吞噬了;)。
但是,如果您可以将文件编写为UTF16-LE格式,则有一个实际解决方案。使用\t分隔符而不指定sep,Excel就可以正常工作。 输入(采用UTF16-LE编码编写)
\ufeffSome;Header;Columns
Wîth;Fàncÿ;Stûff

输出

|Some|Header|Columns|
|Wîth|Fàncÿ |Stûff  |

1
我还不能写评论,但我想提到@Pier-Luc Gendreau的解决方案。虽然在欧洲版Excel中打开它是可能的(默认使用;作为分隔符)并且具有完整的utf-16LE支持,但是当您指定sep=,时,似乎无法使用此技术。

该解决方案的问题在于,尽管Excel正确解释了sep =;,但它会在最后一行的第一列中显示sep =(是的,它吞下了;)。

对我而言,如果我指定的分隔符不是默认值(在我的情况下是;),那么它就不起作用,因此我认为Excel没有正确解释最后一行,并且吞掉了最后一个分隔符,因为这是默认行为。 如果我错了,请纠正我

我认为你说的并不错,但我也不认为这使我的答案是错误的!这只是一个针对非常特定问题的解决方法,因此它取决于您的确切需求。 - Pier-Luc Gendreau

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