【问题标题】:JavaScript array to CSVJavaScript 数组到 CSV
【发布时间】:2013-09-21 19:59:54
【问题描述】:

我已经关注了这篇帖子How to export JavaScript array info to csv (on client side)? 来获取一个嵌套的 js 数组,该数组写为 csv 文件。

数组看起来像:

var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];

链接中给出的代码运行良好,只是在 csv 文件的第三行之后,所有其余值都在同一行 例如

名称1,2,3
名称2,4,5
名称3,6,7
name4,8,9name5,10,11 等

谁能解释这是为什么?同样使用 Chrome 或 FF。

谢谢

编辑

jsfiddle http://jsfiddle.net/iaingallagher/dJKz6/

伊恩

【问题讨论】:

    标签: javascript arrays csv


    【解决方案1】:

    如果你使用 React 并且想在客户端生成一个 CSV 文件,你可以这样做:

    import CsvGenerator from '@exlabs/react-csv-generator';
    
    const data = [{ id: 1, name: 'first' }, { id: 2, name: 'second' }];
    
    const MyComponent = () => {
      return (
        <CsvGenerator fileName="my-name" items={data}>
          Download!
        </CsvGenerator>
      );
    };
    

    重要的是,这个包很轻,支持 Excel 和 Numbers。

    Link to the npm package

    【讨论】:

      【解决方案2】:

      ES6:

      let csv = test_array.map(row=>row.join(',')).join('\n') 
      //test_array being your 2D array
      

      【讨论】:

        【解决方案3】:

        引用的答案是错误的。你必须改变

        csvContent += index < infoArray.length ? dataString+ "\n" : dataString;
        

        csvContent += dataString + "\n";
        

        至于为什么引用的答案是错误的(有趣的是它被接受了!):indexforEach回调函数的第二个参数,是循环数组中的索引,没有意义将其与infoArray 的大小进行比较,infoArray 是所述数组的一项(恰好也是一个数组)。

        编辑

        自从我写下这个答案以来,已经过去了六年。许多事情都发生了变化,包括浏览器。以下是部分答案:

        老化部分的开始

        顺便说一句,引用的代码不是最佳的。您应该避免重复附加到字符串。您应该改为附加到数组,并在最后执行 array.join("\n") 。像这样:

        var lineArray = [];
        data.forEach(function (infoArray, index) {
            var line = infoArray.join(",");
            lineArray.push(index == 0 ? "data:text/csv;charset=utf-8," + line : line);
        });
        var csvContent = lineArray.join("\n");
        

        老化部分结束

        (请记住,CSV 大小写与通用字符串连接有点不同,因为您还必须为每个字符串添加分隔符。)

        不管怎样,上述情况似乎不再正确,至少对于 Chrome 和 Firefox 而言并非如此(不过对于 Safari 来说似乎仍然如此)。

        为了结束不确定性,我写了一个jsPerf test 来测试,为了以逗号分隔的方式连接字符串,将它们推入数组并加入数组,或者先连接它们是否更快使用逗号,然后使用 += 运算符直接使用结果字符串。

        请点击链接并运行测试,以便我们有足够的数据来谈论事实而不是意见。

        【讨论】:

        • 您确定字符串 += 不是最优的吗? This article 似乎另有说法。
        • fname 怎么样?这些文件只是命名为下载,而不是 .csv 文件
        • 如果其中一个值中有'\n'或',',它将中断。
        • @WalterTross 完美!谢谢!那完美无瑕!此外,我使用链接 [1] 下载内容。基本上使用BlobURL。 [1]stackoverflow.com/a/24922761/334872
        • 并且,使用这个解决方案,我删除了 csvContentArray 的第一个条目(“assigning data:text.......”),因为它不再需要了。
        【解决方案4】:

        对于一个简单的 csv,一个 map() 和一个 join() 就足够了:

        var csv = test_array.map(function(d){
            return d.join();
        }).join('\n');
        
        /* Results in 
        name1,2,3
        name2,4,5
        name3,6,7
        name4,8,9
        name5,10,11
        

        此方法还允许您在内部join 中指定除逗号以外的列分隔符。例如标签:d.join('\t')

        另一方面,如果您想正确执行此操作并将字符串括在引号 "" 中,那么您可以使用一些 JSON 魔术:

        var csv = test_array.map(function(d){
           return JSON.stringify(d);
        })
        .join('\n') 
        .replace(/(^\[)|(\]$)/mg, ''); // remove opening [ and closing ] brackets from each line 
        
        /* would produce
        "name1",2,3
        "name2",4,5
        "name3",6,7
        "name4",8,9
        "name5",10,11
        

        如果你有像这样的对象数组:

        var data = [
          {"title": "Book title 1", "author": "Name1 Surname1"},
          {"title": "Book title 2", "author": "Name2 Surname2"},
          {"title": "Book title 3", "author": "Name3 Surname3"},
          {"title": "Book title 4", "author": "Name4 Surname4"}
        ];
        
        // use
        var csv = data.map(function(d){
            return JSON.stringify(Object.values(d));
        })
        .join('\n') 
        .replace(/(^\[)|(\]$)/mg, '');
        

        【讨论】:

        【解决方案5】:
        const escapeString = item => (typeof item === 'string') ? `"${item}"` : String(item)
        
        const arrayToCsv = (arr, seperator = ';') => arr.map(escapeString).join(seperator)
        
        const rowKeysToCsv = (row, seperator = ';') => arrayToCsv(Object.keys(row))
        
        const rowToCsv = (row, seperator = ';') => arrayToCsv(Object.values(row))
        
        const rowsToCsv = (arr, seperator = ';') => arr.map(row => rowToCsv(row, seperator)).join('\n')
        
        const collectionToCsvWithHeading = (arr, seperator = ';') => `${rowKeysToCsv(arr[0], seperator)}\n${rowsToCsv(arr, seperator)}`
        
        
        // Usage: 
        
        collectionToCsvWithHeading([
          { title: 't', number: 2 },
          { title: 't', number: 1 }
        ])
        
        // Outputs: 
        "title";"number"
        "t";2
        "t";1
        

        【讨论】:

        • 对我不起作用
        【解决方案6】:

        以下代码是用 ES6 编写的,它可以在大多数浏览器中正常运行。

        var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];
        
        // Construct the comma seperated string
        // If a column values contains a comma then surround the column value by double quotes
        const csv = test_array.map(row => row.map(item => (typeof item === 'string' && item.indexOf(',') >= 0) ? `"${item}"`: String(item)).join(',')).join('\n');
        
        // Format the CSV string
        const data = encodeURI('data:text/csv;charset=utf-8,' + csv);
        
        // Create a virtual Anchor tag
        const link = document.createElement('a');
        link.setAttribute('href', data);
        link.setAttribute('download', 'export.csv');
        
        // Append the Anchor tag in the actual web page or application
        document.body.appendChild(link);
        
        // Trigger the click event of the Anchor link
        link.click();
        
        // Remove the Anchor link form the web page or application
        document.body.removeChild(link);

        【讨论】:

        • 如果数据有 # 字符失败。代替 encodeUri,encodeUriComponent 解决了 # 字符的问题。 `encodeURIComponent('a#b') "a%23b" encodeURI('a#b') "a#b" `
        【解决方案7】:

        我创建了这段代码来创建一个漂亮、可读的 csv 文件:

        var objectToCSVRow = function(dataObject) {
            var dataArray = new Array;
            for (var o in dataObject) {
                var innerValue = dataObject[o]===null?'':dataObject[o].toString();
                var result = innerValue.replace(/"/g, '""');
                result = '"' + result + '"';
                dataArray.push(result);
            }
            return dataArray.join(' ') + '\r\n';
        }
        
        var exportToCSV = function(arrayOfObjects) {
        
            if (!arrayOfObjects.length) {
                return;
            }
        
            var csvContent = "data:text/csv;charset=utf-8,";
        
            // headers
            csvContent += objectToCSVRow(Object.keys(arrayOfObjects[0]));
        
            arrayOfObjects.forEach(function(item){
                csvContent += objectToCSVRow(item);
            }); 
        
            var encodedUri = encodeURI(csvContent);
            var link = document.createElement("a");
            link.setAttribute("href", encodedUri);
            link.setAttribute("download", "customers.csv");
            document.body.appendChild(link); // Required for FF
            link.click();
            document.body.removeChild(link); 
        }
        

        在您的情况下,由于您在数组中使用数组而不是数组中的对象,因此您将跳过标题部分,但您可以自己添加列名,方法是放置这个而不是那个部分:

        // headers
        csvContent += '"Column name 1" "Column name 2" "Column name 3"\n';
        

        秘诀是用空格分隔 csv 文件中的列,我们将列值放在双引号中以允许空格,并转义值本身中的任何双引号。

        另外请注意,我用空字符串替换空值,因为这符合我的需要,但您可以更改它并用您喜欢的任何内容替换它。

        【讨论】:

          【解决方案8】:

          选择的答案可能是正确的,但似乎不必要地不清楚。

          我发现Shomz's Fiddle 非常有帮助,但又一次不必要地不清楚。 (编辑:我现在看到 Fiddle 是基于 OP 的 Fiddle。)

          这是我认为更清楚的版本(我已经为它创建了一个Fiddle):

          function downloadableCSV(rows) {
            var content = "data:text/csv;charset=utf-8,";
          
            rows.forEach(function(row, index) {
              content += row.join(",") + "\n";
            });
          
            return encodeURI(content);
          }
          
          var rows = [
            ["name1", 2, 3],
            ["name2", 4, 5],
            ["name3", 6, 7],
            ["name4", 8, 9],
            ["name5", 10, 11]
          ];
          
          $("#download").click(function() {
            window.open(downloadableCSV(rows));
          });
          

          【讨论】:

            【解决方案9】:

            如果您的数据包含任何换行符或逗号,您需要先将其转义:

            const escape = text =>
                text.replace(/\\/g, "\\\\")
                    .replace(/\n/g, "\\n")
                    .replace(/,/g, "\\,")
            
            escaped_array = test_array.map(fields => fields.map(escape))
            

            然后简单地做:

            csv = escaped_array.map(fields => fields.join(","))
                            .join("\n")
            

            如果你想让它在浏览器中可下载:

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

            【讨论】:

            • 转义函数可能不是 100% 正确,但它应该为您指明正确的方向。
            • 要在 CSV 中包含新行 (\n),您只需将包含新行的字符串用引号括起来。要将字符串括在引号中,您需要转义此字符串中的引号)
            • 错误:fields.map 不是函数
            【解决方案10】:

            一般形式是:

            var ids = []; <= this is your array/collection
            var csv = ids.join(",");
            

            对于您的情况,您将不得不稍作调整

            【讨论】:

            • 此答案假定要导出的数组中没有“特殊”字符(没有逗号,没有引号)。如果有,此答案将不起作用。
            猜你喜欢
            • 2014-11-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-11-02
            • 2014-07-08
            • 2016-10-15
            • 2013-04-05
            • 1970-01-01
            相关资源
            最近更新 更多