Javascript 打开Excel和TextEdit时UTF8 CSV文件的编码问题

Javascript 打开Excel和TextEdit时UTF8 CSV文件的编码问题,javascript,excel,csv,encoding,utf-8,Javascript,Excel,Csv,Encoding,Utf 8,我最近添加了一个CSV下载按钮,它从数据库(Postgres)和服务器(Ruby on Rails)的数组中获取数据,并在客户端将其转换为CSV文件(Javascript、HTML5)。我目前正在测试CSV文件,遇到了一些编码问题 当我通过“less”查看CSV文件时,该文件看起来很好。但当我在Excel或TextEdit中打开文件时,我开始看到奇怪的字符,如 -欧元,欧元,欧元 出现在文本中。基本上,我看到这里描述的字符: 我了解到,当数据库编码设置设置错误时,可能会出现这种问题。但是,我使用

我最近添加了一个CSV下载按钮,它从数据库(Postgres)和服务器(Ruby on Rails)的数组中获取数据,并在客户端将其转换为CSV文件(Javascript、HTML5)。我目前正在测试CSV文件,遇到了一些编码问题

当我通过“less”查看CSV文件时,该文件看起来很好。但当我在Excel或TextEdit中打开文件时,我开始看到奇怪的字符,如

-欧元,欧元,欧元

出现在文本中。基本上,我看到这里描述的字符:

我了解到,当数据库编码设置设置错误时,可能会出现这种问题。但是,我使用的数据库设置为使用UTF8编码。当我调试创建CSV文件的JS代码时,文本显示正常。(这可能是一个Chrome功能,而不是其他功能)

我感到很沮丧,因为我从网上搜索中学到的唯一一件事是,编码不起作用的原因可能有很多,我不确定哪一部分是错的(请原谅,我最初标记了很多东西),而且我尝试的任何东西都没有对我的问题提供新的解释

以下是创建CSV文件的JavaScript代码段,仅供参考

$(document).ready(function() {
var csvData = <%= raw to_csv(@view_scope, clicks_post).as_json %>;
var csvContent = "data:text/csv;charset=utf-8,";
csvData.forEach(function(infoArray, index){
  var dataString = infoArray.join(",");
  csvContent += dataString+ "\n";
}); 
var encodedUri = encodeURI(csvContent);
var button = $('<a>');
button.text('Download CSV');
button.addClass("button right");
button.attr('href', encodedUri);
button.attr('target','_blank');
button.attr('download','<%=title%>_25_posts.csv');
$("#<%=title%>_download_action").append(button);
});
$(文档).ready(函数(){
var csvData=;
var csvContent=“数据:文本/csv;字符集=utf-8,”;
forEach(函数(infoArray,索引){
var dataString=infoArray.join(“,”);
csvContent+=数据字符串+“\n”;
}); 
var encodedUri=encodeURI(csvContent);
var按钮=$('');
文本(“下载CSV”);
addClass(“按钮右侧”);
button.attr('href',encodedUri);
button.attr('target','u blank');
button.attr('download','u 25_posts.csv');
$(“##_下载_操作”).append(按钮);
});

当@jlarson更新信息说Mac是最大的罪魁祸首时,我们可能会进一步了解。Office for Mac在导入文件时对读取Unicode格式的支持相当差,至少在2011年前后是如此

对UTF-8的支持似乎几乎不存在,我读过一些关于它工作的评论,而大多数人说它不存在。不幸的是,我没有任何Mac电脑可供测试。同样:文件本身应该是UTF-8,但是导入会停止这个过程

用Javascript编写了一个快速测试,用于导出UTF-16 little和big-endian,带/不带BOM等

代码可能应该重构,但应该可以进行测试。它可能比UTF-8工作得更好。当然,这通常也意味着更大的数据传输,因为任何标志符都是两个或四个字节

你可以在这里找到小提琴:

—, ”, “
请注意,它不会以任何特定方式处理CSV。它主要用于纯转换为具有UTF-8、UTF-16大/小尾端和+/-BOM的数据URL。小提琴中有一个选项可以用制表符代替逗号,但相信如果这个解决方案可行的话,那将是一个相当粗糙和脆弱的解决方案


通常使用如下方式:

对象有两个结果属性:

1.
encoder.lead

—, ”, “
这是数据URL的mime类型、字符集等。根据传递给初始值设定项的选项构建,也可以说
.config({…new conf…}).intro()
来重新构建

data:[<MIME-type>][;charset=<encoding>][;base64]

先前的答复:
我没有任何复制您的设置,但是如果您的案例与@jlarson相同,那么生成的文件应该是正确的

这个答案有点长,(你说的有趣的话题?),但是围绕这个问题讨论各个方面,什么(可能)正在发生,以及如何以各种方式实际检查正在发生的事情

TL;博士: 文本可能作为ISO-8859-1、Windows-1252或类似文件导入,而不是作为UTF-8导入。通过使用导入或其他方式强制应用程序将文件读取为UTF-8


附言:这是一个很好的工具,可以在这个旅程

长途跋涉 要100%确定我们正在查看的内容,最“简单”的方法是对结果使用十六进制编辑器。或者从命令行中使用
hextump
xxd
等来查看文件。在这种情况下,字节序列应该是从脚本交付的UTF-8的字节序列

例如,如果我们使用jlarson的脚本,它将使用
data
数组:

这一个被合并到字符串中:

 name,city,state<newline>
 \u0500\u05E1\u0E01\u1054,seattle,washington<newline>
查看下载文件的十六进制转储:

0000000: 6e61 6d65 2c63 6974 792c 7374 6174 650a  name,city,state.
0000010: d480 d7a1 e0b8 81e1 8194 2c73 6561 7474  ..........,seatt
0000020: 6c65 2c77 6173 6869 6e67 746f 6e0a       le,washington.
在第二行中,我们发现与上述匹配的
d480 d7a1 e0b8 81e1 8194

0000010: d480  d7a1  e0b8 81  e1 8194 2c73 6561 7474  ..........,seatt
         |   | |   | |     |  |     |  | |  | |  | |
         +-+-+ +-+-+ +--+--+  +--+--+  | |  | |  | |
           |     |      |        |     | |  | |  | |
           Ԁ     ס      ก        ၔ     , s  e a  t t
其他字符也没有损坏

如果你想做类似的测试。结果应该是相似的


根据提供的样本

我们也可以看看问题中提供的样品。很可能假设文本由代码页1252在Excel/TextEdit中表示

在Windows-1252上引用维基百科:

Windows-1252或CP-1252是拉丁字母的字符编码,由 Microsoft Windows的旧组件中的默认值(英语和其他语言) 西方语言。它是Windows代码页组中的一个版本。 在乳胶包装中,它被称为“ansinew”

检索原始字节 要将其转换回原始形式,我们可以查看以下内容:

Character:   <â>  <€>  <”>  <,>  < >  <â>  <€>  < >  <,>  < >  <â>  <€>  <œ>
U.Hex    :    e2 20ac 201d   2c   20   e2 20ac   9d   2c   20   e2 20ac  153
T.Hex    :    e2   80   94   2c   20   e2   80   9d*  2c   20   e2   80   9c
9d
这样的特殊情况在CP-1252中没有相应的代码点,我们只是直接复制

注意:如果通过将文本复制到文件并进行十六进制转储来查看损坏的字符串,请使用UTF-16编码等方式保存该文件,以获得表中所示的Unicode值。例如,在Vim中:

set fenc=utf-16
# Or
set fenc=ucs-2
字节到UTF-8 然后,我们将结果,
T.Hex
行合并到UTF-8中。在UTF-8序列中,字节由a表示。例如,如果一个字节具有二进制值
110x xxxx
,我们知道这个字节和下一个字节代表一个代码点。一共两个<代码>1110 xxxx
告诉我们它是
0000010: d480  d7a1  e0b8 81  e1 8194 2c73 6561 7474  ..........,seatt
         |   | |   | |     |  |     |  | |  | |  | |
         +-+-+ +-+-+ +--+--+  +--+--+  | |  | |  | |
           |     |      |        |     | |  | |  | |
           Ԁ     ס      ก        ၔ     , s  e a  t t
Character:   <â>  <€>  <”>  <,>  < >  <â>  <€>  < >  <,>  < >  <â>  <€>  <œ>
U.Hex    :    e2 20ac 201d   2c   20   e2 20ac   9d   2c   20   e2 20ac  153
T.Hex    :    e2   80   94   2c   20   e2   80   9d*  2c   20   e2   80   9c
â => Unicode 0xe2   => CP-1252 0xe2
” => Unicode 0x201d => CP-1252 0x94
€ => Unicode 0x20ac => CP-1252 0x80
set fenc=utf-16
# Or
set fenc=ucs-2
0xe2 = 1110 0010bin => 3 bytes => 0xe28094 (em-dash) — 0x2c = 0010 1100bin => 1 byte => 0x2c (comma) , 0x2c = 0010 0000bin => 1 byte => 0x20 (space) 0xe2 = 1110 0010bin => 3 bytes => 0xe2809d (right-dq) ” 0x2c = 0010 1100bin => 1 byte => 0x2c (comma) , 0x2c = 0010 0000bin => 1 byte => 0x20 (space) 0xe2 = 1110 0010bin => 3 bytes => 0xe2809c (left-dq) “
—, ”, “
UTF-8: e2 80 94 2c 20 e2 80 9d 2c 20 e2 80 9c
e2 => â
80 => €
94 => ”
2c => ,
20 => <space>
...
—, â€, “
Data -> Import External Data -> Import Data
>> encodeURI('Ԁסกၔ,seattle,washington')
<< "%D4%80%D7%A1%E0%B8%81%E1%81%94,seattle,washington"
%D4%80%D7%A1%E0%B8%81%E1%81%94 (encodeURI in log)
 d4 80 d7 a1 e0 b8 81 e1 81 94 (hex-dump of file)
>> encodeURI('It could be a problem in your server encoding.

You could try (assuming locale english US) if you are running Linux:

sudo locale-gen en_US en_US.UTF-8
dpkg-reconfigure locales
var csvHeader = 'data:text/csv;charset=iso-8859-1;base64,'
var encodedCsv =  CryptoJS.enc.Latin1.parse(csvData).toString(CryptoJS.enc.Base64)
var dataURI = csvHeader + encodedCsv
var mystring = myString.replace(/\u200B/g,'');
button.href = 'data:' + mimeType + ';charset=UTF-8,%ef%bb%bf' + encodedUri;