gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 16:55:40 26 4
gpt4 key购买 nike

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

当我通过“less”查看 CSV 文件时,文件显示正常。但是当我在 Excel 或 TextEdit 中打开文件时,我开始看到奇怪的字符,例如

—, â€, “



出现在文中。基本上,我看到这里描述的字符: http://digwp.com/2011/07/clean-up-weird-characters-in-database/

我读到当数据库编码设置设置为错误时可能会出现此类问题。但是,我使用的数据库设置为使用 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);
});

最佳答案

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

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

用 Javascript 编写了一个快速测试,用于导出百分比转义 UTF-16 小端和大端,带/不带 BOM 等。

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

你可以在这里找到一个 fiddle :

Unicode export sample Fiddle



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

通常使用如下:
// Initiate
encoder = new DataEnc({
mime : 'text/csv',
charset: 'UTF-16BE',
bom : true
});

// Convert data to percent escaped text
encoder.enc(data);

// Get result
var result = encoder.pay();

对象有两个结果属性:

1.) encoder.lead

这是数据 URL 的 MIME 类型、字符集等。从传递给初始化程序的选项构建,或者也可以说 .config({ ... new conf ...}).intro()重新构建。
data:[<MIME-type>][;charset=<encoding>][;base64]

您可以指定 base64,但没有 base64 转换(至少目前没有)。

2.) encoder.buf

这是一个带有百分比转义数据的字符串。
.pay()函数简单地返回 1.) 和 2.) 作为一。

主要代码:
function DataEnc(a) {
this.config(a);
this.intro();
}
/*
* http://www.iana.org/assignments/character-sets/character-sets.xhtml
* */
DataEnc._enctype = {
u8 : ['u8', 'utf8'],
// RFC-2781, Big endian should be presumed if none given
u16be : ['u16', 'u16be', 'utf16', 'utf16be', 'ucs2', 'ucs2be'],
u16le : ['u16le', 'utf16le', 'ucs2le']
};
DataEnc._BOM = {
'none' : '',
'UTF-8' : '%ef%bb%bf', // Discouraged
'UTF-16BE' : '%fe%ff',
'UTF-16LE' : '%ff%fe'
};
DataEnc.prototype = {
// Basic setup
config : function(a) {
var opt = {
charset: 'u8',
mime : 'text/csv',
base64 : 0,
bom : 0
};
a = a || {};
this.charset = typeof a.charset !== 'undefined' ?
a.charset : opt.charset;
this.base64 = typeof a.base64 !== 'undefined' ? a.base64 : opt.base64;
this.mime = typeof a.mime !== 'undefined' ? a.mime : opt.mime;
this.bom = typeof a.bom !== 'undefined' ? a.bom : opt.bom;

this.enc = this.utf8;
this.buf = '';
this.lead = '';
return this;
},
// Create lead based on config
// data:[<MIME-type>][;charset=<encoding>][;base64],<data>
intro : function() {
var
g = [],
c = this.charset || '',
b = 'none'
;
if (this.mime && this.mime !== '')
g.push(this.mime);
if (c !== '') {
c = c.replace(/[-\s]/g, '').toLowerCase();
if (DataEnc._enctype.u8.indexOf(c) > -1) {
c = 'UTF-8';
if (this.bom)
b = c;
this.enc = this.utf8;
} else if (DataEnc._enctype.u16be.indexOf(c) > -1) {
c = 'UTF-16BE';
if (this.bom)
b = c;
this.enc = this.utf16be;
} else if (DataEnc._enctype.u16le.indexOf(c) > -1) {
c = 'UTF-16LE';
if (this.bom)
b = c;
this.enc = this.utf16le;
} else {
if (c === 'copy')
c = '';
this.enc = this.copy;
}
}
if (c !== '')
g.push('charset=' + c);
if (this.base64)
g.push('base64');
this.lead = 'data:' + g.join(';') + ',' + DataEnc._BOM[b];
return this;
},
// Deliver
pay : function() {
return this.lead + this.buf;
},
// UTF-16BE
utf16be : function(t) { // U+0500 => %05%00
var i, c, buf = [];
for (i = 0; i < t.length; ++i) {
if ((c = t.charCodeAt(i)) > 0xff) {
buf.push(('00' + (c >> 0x08).toString(16)).substr(-2));
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
} else {
buf.push('00');
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
}
}
this.buf += '%' + buf.join('%');
// Note the hex array is returned, not string with '%'
// Might be useful if one want to loop over the data.
return buf;
},
// UTF-16LE
utf16le : function(t) { // U+0500 => %00%05
var i, c, buf = [];
for (i = 0; i < t.length; ++i) {
if ((c = t.charCodeAt(i)) > 0xff) {
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
buf.push(('00' + (c >> 0x08).toString(16)).substr(-2));
} else {
buf.push(('00' + (c & 0xff).toString(16)).substr(-2));
buf.push('00');
}
}
this.buf += '%' + buf.join('%');
// Note the hex array is returned, not string with '%'
// Might be useful if one want to loop over the data.
return buf;
},
// UTF-8
utf8 : function(t) {
this.buf += encodeURIComponent(t);
return this;
},
// Direct copy
copy : function(t) {
this.buf += t;
return this;
}
};

上一个答案:

我没有任何设置来复制您的设置,但是如果您的情况与@jlarson 相同,那么生成的文件应该是正确的。

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

特尔;博士:

文本很可能以 ISO-8859-1、Windows-1252 等格式导入,而不是以 UTF-8 格式导入。使用导入或其他方式强制应用程序以 UTF-8 格式读取文件。

PS: The UniSearcher是一个很好的工具,可以在这次旅程中使用。

漫漫长路

100% 确定我们正在查看的“最简单”方法是对结果使用十六进制编辑器。或者使用 hexdump , xxd或类似从命令行查看文件。在这种情况下,字节序列应该是脚本提供的 UTF-8 的字节序列。

例如,如果我们采用 jlarson 的脚本,则它采用 data大批:
data = ['name', 'city', 'state'],
['\u0500\u05E1\u0E01\u1054', 'seattle', 'washington']

这个合并到字符串中:
 name,city,state<newline>
\u0500\u05E1\u0E01\u1054,seattle,washington<newline>

它由 Unicode 转换为:
 name,city,state<newline>
Ԁסกၔ,seattle,washington<newline>

由于 UTF-8 使用 ASCII 作为基数(未设置最高位的字节与 ASCII 中的相同),因此测试数据中唯一的特殊序列是“Ԁסกၔ”,它依次是:
Code-point  Glyph      UTF-8
----------------------------
U+0500 Ԁ d4 80
U+05E1 ס d7 a1
U+0E01 ก e0 b8 81
U+1054 ၔ e1 81 94

查看下载文件的十六进制转储:
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

其他字符也没有被破坏。

如果你愿意,可以做类似的测试。结果应该是相似的。

通过提供 sample —, â€, “
我们还可以查看问题中提供的示例。很可能假设文本在 Excel/TextEdit 中由代码页 1252 表示。

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

Windows-1252 or CP-1252 is a character encoding of the Latin alphabet, used by default in the legacy components of Microsoft Windows in English and some other Western languages. It is one version within the group of Windows code pages. In LaTeX packages, it is referred to as "ansinew".



检索原始字节

要将其翻译回其原始形式,我们可以查看 code page layout ,从中我们得到:
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
  • U Unicode 的缩写
  • T是翻译
  • 的缩写

    例如:
    â => Unicode 0xe2   => CP-1252 0xe2
    ” => Unicode 0x201d => CP-1252 0x94
    € => Unicode 0x20ac => CP-1252 0x80

    特殊情况,如 9d在 CP-1252 中没有对应的代码点,我们直接复制。

    注意:如果通过将文本复制到文件并执行十六进制转储来查看损坏的字符串,请使用例如 UTF-16 编码保存文件以获取表中表示的 Unicode 值。例如。在 Vim 中:
    set fenc=utf-16
    # Or
    set fenc=ucs-2

    字节转 UTF-8

    然后我们结合结果, T.Hex行,转换为 UTF-8。在 UTF-8 序列中,字节由 leading byte telling us how many subsequent bytes make the glyph 表示。 .例如,如果一个字节具有二进制值 110x xxxx我们知道这个字节和下一个字节代表一个代码点。一共两个。 1110 xxxx告诉我们它是三个等等。 ASCII 值没有设置高位,因此任何字节匹配 0xxx xxxx是一个独立的。一共一个字节。
    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)  “

    Conclusion; The original UTF-8 string was:

    —, ”, “

    把它弄回来

    我们也可以反过来。原始字符串为字节:
    UTF-8: e2 80 94 2c 20 e2 80 9d 2c 20 e2 80 9c

    cp-1252中的对应值:
    e2 => â
    80 => €
    94 => ”
    2c => ,
    20 => <space>
    ...

    依此类推,结果:
    —, â€, “

    导入到 MS Excel

    换句话说:手头的问题可能是如何将 UTF-8 文本文件导入 MS Excel 和其他一些应用程序。在 Excel 中,这可以通过多种方式完成。
  • 方法一:

  • 不要使用应用程序识别的扩展名保存文件,例如 .csv , 或 .txt ,但完全省略它或弥补一些东西。

    例如将文件另存为 "testfile" ,没有扩展名。然后在 Excel 中打开文件,确认我们确实要打开这个文件,瞧,我们得到了编码选项。选择UTF-8,应该可以正确读取文件。
  • 方法二:

  • 使用导入数据而不是打开文件。就像是:
    Data -> Import External Data -> Import Data

    选择编码并继续。

    检查 Excel 和所选字体是否实际支持字形

    我们还可以使用有时更友好的剪贴板来测试对 Unicode 字符的字体支持。例如,将此页面中的文本复制到 Excel 中:
  • page with code points U+0E00 to U+0EFF

  • 如果存在对代码点的支持,则文本应该可以正常呈现。

    Linux

    在 Linux 上,它在用户空间中主要是 UTF-8,这应该不是问题。使用 Libre Office Calc、Vim 等显示正确呈现的文件。

    为什么它有效(或应该)

    encodeURI从规范状态,(另请阅读 sec-15.1.3 ):

    The encodeURI function computes a new version of a URI in which each instance of certain characters is replaced by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.



    我们可以简单地在我们的控制台中测试它,例如说:
    >> 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)

    或者,测试一个 4 字节的代码:
    >> encodeURI('󱀁')
    << "%F3%B1%80%81"

    如果这不符合

    如果这些都不适用,如果您添加它可能会有所帮助
  • 预期输入与损坏输出的示例(复制粘贴)。
  • 原始数据与结果文件的十六进制转储示例。
  • 关于javascript - 打开 Excel 和 TextEdit 时 UTF8 CSV 文件的编码问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21342492/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com