我目前正在使用 XSLT 开发 CSV 导出。在我的例子中,CSV 文件将与 Excel 一起使用 %99%,因此我必须考虑 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>
(此分隔符未作为硬编码实现)
但是我找不到任何解决方案的问题是,如果您添加“sep=;”或“sep=”文件开头,而使用 UT8-BOM 生成 CSV 文件时,BOM 不再有助于正确显示特殊字符!我确信 BOM 字节始终位于字节数组的开头。此屏幕截图来自 Mac OS X 中的 MS Excel:
前 3 个符号属于 BOM 标头。
您有遇到过这样的问题吗?或者您有什么建议吗?谢谢你。
编辑:
我分享打印屏幕。
a。带 BOM 和
<xsl:text>sep=;</xsl:text>
b。只需BOM
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>
您是对的,当有人双击 CSV 文件时,Excel 2007 中无法让它在不同区域设置中正确加载编码和分隔符。
当您在 BOM 之后指定
sep=
时,它似乎忘记了 BOM 已告诉它它是 UTF-8。
您必须指定 BOM,因为在某些区域设置中 Excel 无法检测分隔符。例如,在丹麦语中,默认分隔符是
;
。如果您输出制表符或逗号分隔的文本,则它不会检测分隔符,而在其他语言环境中,如果您用分号分隔,则不会加载。您可以通过更改 Windows 设置中的区域设置格式来测试这一点 - Excel 然后会选择它。
从这个问题: 是否可以强制 Excel 自动识别 UTF-8 CSV 文件?
答案似乎唯一的方法是使用 UTF-16 LE 编码 with BOM。
另请注意,根据 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
和编码。
这是我用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 |
我还不能写评论,但我想谈谈@Pier-Luc Gendreau 的解决方案。虽然可以在欧洲 Excel 中打开它(默认情况下使用
;
作为分隔符)并具有完整的 utf-16LE 支持,但当您指定 sep=,
时,显然不可能使用此技术。
解决方案的问题是,虽然 Excel 解释 sep=;正确地,它在最后一行的第一列中显示 sep= (是的,它吞下了 ;)。
对我来说,如果我指定的分隔符不是默认的分隔符(在我的例子中是
;
),它就不起作用,所以我假设Excel没有正确解释最后一行并吞下了最后一个分隔符,因为这是默认行为。
如有错误请指正
sep=
功能至今仍被破坏。在最后一行写入 sep=
似乎不适用于最新的 Excel
版本。
但由于某种原因,对于
Excel
编码的 \t
文件,UTF-16LE
始终与分隔符 csv
一起使用。为了能够生成可由具有不同分隔符设置的用户打开的 csv
文件,同时仍保持特殊字符的正确编码,请执行以下操作:
const csv_content = '\ufeff' + 'Micr°$°ft_süüüücks'; // includes \ufeff byte order mark
const utf16le = new Uint16Array(
Array.from(csv_with_translated_headers).map((char) => char.charCodeAt(0)),
);
const blob = new Blob([utf16le], { type: 'text/csv' });