使用JavaScript将JSON导出为带有UTF-8编码(例如希腊语)的CSV或Excel。

18

我正在尝试将一个JSON对象导出并下载为CSV文件,但是在希腊字符方面存在问题。我的代码能够工作,虽然它并不完美。

问题在于希腊字符看起来像垃圾字符。

以下是我现有的代码:

function downloadJsonToCsv(jsonObject) {
    var array = typeof jsonObject != "object" ? JSON.parse(jsonObject) : jsonObject;

    if (array == null) {
        return; // No data found on the jsonObject
    }

    var str = "";

    for (var i = 0; i < array.length; i++) {
        var line = "";

        for (var index in array[i]) {
            line += array[i][index] + ";"; // Set delimiter
        }

        // Here is an example where you would wrap the values in double quotes
        // for (var index in array[i]) {
        //    line += '"' + array[i][index] + '",';
        // }

        line.slice(0,line.Length-1); 

        str += line + "\r\n";
    }

    window.open("data:text/csv;charset=utf-8," + encodeURI(str));
}

我有两个问题。

  1. 如何导出带有正确希腊字符的 CSV 文件?
  2. 如何将此数据导出为 Excel 格式而不是 CSV 格式?

尝试使用encodeURIComponent()而不是encodeURI()。Csv可以被Excel处理,所以我认为你不必改变什么。 - Alkis Kalogeris
如果您正尝试将CSV文件加载到Excel中,请注意Excel无法很好地加载带有非Ascii字符集的CSV文件。确保您使用UTF-8编码保存它,并确保在文件开头添加一个UTF-8 BOM字符。这应该会有所帮助。还请参阅此处许多其他关于CSV + utf8 + Excel的问题。这是一个常见的问题。 - Spudley
当我用记事本打开下载的csv文件时,它看起来很好。如果将其保存为utf-8格式的csv文件,则在Excel中也会显示正常。但是,如果不通过记事本直接在Excel中打开,则会出现问题。我能否通过JavaScript导出utf-8格式的文件? - A. Zalonis
对不起 @alkis,但它没有起作用。 - A. Zalonis
请参考以下链接:https://dev59.com/AnVC5IYBdhLWcg3w4Vb6,了解BOM的详细描述。请注意,如果您使用的是Mac Excel 2011(或更早版本),它会尽可能地忽略BOM。 - stvsmth
显示剩余2条评论
2个回答

43

导出到CSV

导出带有非ASCII字符的CSV文件需要在文件前加上字节顺序标记(即BOM)。在您的代码中更改:

var str = "";

改为:

var str = "\uFEFF";

您需要一个现代版本的Excel才能识别BOM。如此实用的StackOverflow文章所述,Excel 2003及更早版本将无法正确使用BOM。我目前只能访问Windows上的Excel 2003,因此无法进行测试,但这已经有很好的文档记录。

不幸的是,Macintosh上的Excel 2011就不属于这种“现代Excel”。幸运的是,Google表格会做正确的事情。

直接导出到Excel

以下是实现下面代码的jsFiddle。它生成一个SpreadsheetXml文档。这种方法的优点是您可以变得非常巧妙...添加公式和执行更多Excel特定的操作。

// Test script to generate a file from JavaScript such
// that MS Excel will honor non-ASCII characters.

testJson = [
    {
        "name": "Tony Peña",
        "city": "New York",
        "country": "United States",
        "birthdate": "1978-03-15",
        "amount": 42

    },
    {
        "name": "Ζαλώνης Thessaloniki",
        "city": "Athens",
        "country": "Greece",
        "birthdate": "1987-11-23",
        "amount": 42
    }
];

// Simple type mapping; dates can be hard
// and I would prefer to simply use `datevalue`
// ... you could even add the formula in here.
testTypes = {
    "name": "String",
    "city": "String",
    "country": "String",
    "birthdate": "String",
    "amount": "Number"
};

emitXmlHeader = function () {
    var headerRow =  '<ss:Row>\n';
    for (var colName in testTypes) {
        headerRow += '  <ss:Cell>\n';
        headerRow += '    <ss:Data ss:Type="String">';
        headerRow += colName + '</ss:Data>\n';
        headerRow += '  </ss:Cell>\n';        
    }
    headerRow += '</ss:Row>\n';    
    return '<?xml version="1.0"?>\n' +
           '<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">\n' +
           '<ss:Worksheet ss:Name="Sheet1">\n' +
           '<ss:Table>\n\n' + headerRow;
};

emitXmlFooter = function() {
    return '\n</ss:Table>\n' +
           '</ss:Worksheet>\n' +
           '</ss:Workbook>\n';
};

jsonToSsXml = function (jsonObject) {
    var row;
    var col;
    var xml;
    var data = typeof jsonObject != "object" 
             ? JSON.parse(jsonObject) 
             : jsonObject;

    xml = emitXmlHeader();

    for (row = 0; row < data.length; row++) {
        xml += '<ss:Row>\n';

        for (col in data[row]) {
            xml += '  <ss:Cell>\n';
            xml += '    <ss:Data ss:Type="' + testTypes[col]  + '">';
            xml += data[row][col] + '</ss:Data>\n';
            xml += '  </ss:Cell>\n';
        }

        xml += '</ss:Row>\n';
    }

    xml += emitXmlFooter();
    return xml;  
};

console.log(jsonToSsXml(testJson));

这将生成下面的XML文档。如果将此XML保存在名为test.xls的文件中,Excel应该会识别它并使用正确的编码打开它。

<?xml version="1.0"?>
<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<ss:Worksheet ss:Name="Sheet1">
<ss:Table>

<ss:Row>
  <ss:Cell>
    <ss:Data ss:Type="String">name</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">city</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">country</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">birthdate</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">amount</ss:Data>
  </ss:Cell>
</ss:Row>

<ss:Row>
  <ss:Cell>
    <ss:Data ss:Type="String">Tony Peña</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">New York</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">United States</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">1978-03-15</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="Number">42</ss:Data>
  </ss:Cell>
</ss:Row>
<ss:Row>
  <ss:Cell>
    <ss:Data ss:Type="String">Ζαλώνης Thessaloniki</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">Athens</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">Greece</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="String">1987-11-23</ss:Data>
  </ss:Cell>
  <ss:Cell>
    <ss:Data ss:Type="Number">42</ss:Data>
  </ss:Cell>
</ss:Row>

</ss:Table>
</ss:Worksheet>
</ss:Workbook>

不过我必须承认,如果可能的话,我的强烈倾向是在服务器端完成这个任务。我以前用过Python库openpyxl来做这件事,而且它相当简单。大多数服务器端语言都有一个生成Excel文件的库,它们应该提供比字符串拼接更好的构造。

无论如何,请参考MSDN博客了解基础知识。还有这篇StackOverflow文章介绍了各种选项的优缺点。


1
优秀的回答... +1 - Hackerman
1
对于IE 11,您需要执行window.navigator.msSaveOrOpenBlob(blob,filename);以便下载文件。更新的JSFiddle在这里http://jsfiddle.net/kmqz9/223/。 - Vishnoo Rath
如何为每一列添加标题行(名称,城市,国家,日期)? - user3263194
@user3263194:添加标题行非常简单...你应该自己尝试一下;但我已经更新了答案。 - stvsmth
2
@stv 我试了一下你的fiddle,当下载的文件在Excel中打开时,我收到了这个警告:“'test(4).xls'的文件格式和扩展名不匹配。该文件可能已损坏或不安全。除非您信任其来源,否则不要打开它。您仍要打开它吗?” - ivayloc
显示剩余4条评论

6
为了让Excel读取Unicode CSV文件,您需要在csv中的第一个字符串添加字节顺序标记。通过在代码中添加以下行,可以通过JavaScript来实现此操作:
line="\ufeff"+line

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