将csv-parse输出保存到变量

时间:2018-10-26 17:21:32

标签: javascript node.js

我是刚开始使用csv-parse的人,这个来自项目github的示例满足了我的需要,但有一个例外。而不是通过console.log输出,我想将数据存储在变量中。我尝试将fs行分配给变量,然后返回data而不是将其记录下来,但这只是返回了一大堆我不理解的东西。最终目标是将CSV文件导入SQLite。

var fs = require('fs');
var parse = require('..');

var parser = parse({delimiter: ';'}, function(err, data){
  console.log(data);
});

fs.createReadStream(__dirname+'/fs_read.csv').pipe(parser);

这是我尝试过的:

const fs = require("fs");
const parse = require("./node_modules/csv-parse");

const sqlite3 = require("sqlite3");
// const db = new sqlite3.Database("testing.sqlite");

let parser = parse({delimiter: ","}, (err, data) => {
    // console.log(data);
    return data;
});

const output = fs.createReadStream(__dirname + "/users.csv").pipe(parser);
console.log(output);

2 个答案:

答案 0 :(得分:1)

我还在努力弄清楚如何将csv-parse中的数据返回到调用解析的顶层。具体来说,我试图在处理结束时获取parser.info数据以查看是否成功,但是针对该问题的解决方案也可以根据需要获取行数据。

关键是将所有流事件侦听器包装到Promise中,并在解析器的回调中解析Promise。

function startFileImport(myFile) {

  // THIS IS THE WRAPPER YOU NEED
  return new Promise((resolve, reject) => {

    let readStream = fs.createReadStream(myFile);

    let fileRows = [];
    const parser = parse({
      delimiter: ','
    });

    // Use the readable stream api
    parser.on('readable', function () {
      let record
      while (record = parser.read()) {
        if (record) { fileRows.push(record); }
      }
    });

    // Catch any error
    parser.on('error', function (err) {
      console.error(err.message)
    });

    parser.on('end', function () {
      const { lines } = parser.info;
      // RESOLVE OUTPUT THAT YOU WANT AT PARENT-LEVEL
      resolve({ status: 'Successfully processed lines: ', lines });
    });

    // This will wait until we know the readable stream is actually valid before piping                
    readStream.on('open', function () {
      // This just pipes the read stream to the response object (which goes to the client)
      readStream.pipe(parser);
    });

    // This catches any errors that happen while creating the readable stream (usually invalid names)
    readStream.on('error', function (err) {
      resolve({ status: null, error: 'readStream error' + err });
    });

  });
}

答案 1 :(得分:0)

这个问题表明人们对异步流API感到困惑,似乎至少要问三件事。

  1. 如何获取output包含代表已解析的CSV数据的数组?

由于异步API的运行方式,output不会像您(和许多其他程序员)所希望的那样在顶层存在。整齐地组装在一个地方的所有数据只能存在于回调函数中。从语法上讲,下一个最好的事情是const output = await somePromiseOfOutput(),但这只能在async function中发生,并且只有当我们从流转换为Promise时才能发生。这都是可能的,我提到了它,因此您以后可以自己检查出来。我假设您要坚持使用流。

由所有行组成的数组只能在读取整个流之后存在。这就是为什么所有行仅在作者的“ Stream API”示例中仅在.on('end', ...)回调中可用的原因。如果您想同时对所有行进行任何操作,则需要在结束回调中进行。

https://csv.js.org/parse/api/中注意到作者:

  1. 使用on可读的回调将单个记录放入外部定义为output的先前为空的数组中。
  2. 使用on error回调报告错误
  3. 使用结束回调将输出中的所有累积记录与预期结果进行比较

... const output = [] ... parser.on('readable', function(){ let record while (record = parser.read()) { output.push(record) } }) // Catch any error parser.on('error', function(err){ console.error(err.message) }) // When we are done, test that the parsed output matched what expected parser.on('end', function(){ assert.deepEqual( output, [ [ 'root','x','0','0','root','/root','/bin/bash' ], [ 'someone','x','1022','1022','','/home/someone','/bin/bash' ] ] ) })

  1. 关于与sqlite接口的目标,这实际上是在构建自定义的流终结点。

在这种情况下,implement a customized writable stream接受解析器的输出并将行发送到数据库。

然后您只需将管道调用链接为

fs.createReadStream(__dirname+'/fs_read.csv') .pipe(parser) .pipe(your_writable_stream)

当心此代码立即返回。它不等待操作完成。它与node.js内部的隐藏事件循环进行交互。事件循环经常使来自另一种语言的新开发人员感到困惑,他们习惯了命令式风格,而跳过了他们的node.js培训这一部分。

实现这样的定制可写流可能会变得复杂,并且留给读者练习。如果解析器发出一行,这将是最简单的,然后可以编写编写器来处理单行。确保您能够以某种方式注意到错误并抛出适当的异常,否则您将被不完整的结果所困扰,没有警告或原因。

一种骇人听闻的方法是将console.log(data)中的let parser = ...替换为自定义函数writeRowToSqlite(data),您必须编写该函数才能实现自定义流。由于异步API问题,使用return data并没有任何用处。正如您所看到的,当然不能将数据放入输出变量中。


  1. 关于修改后的帖子中的output为什么不包含数据...

不幸的是,正如您所发现的,这通常是错误的:

const output = fs.createReadStream(__dirname + "/users.csv").pipe(parser); console.log(output);

在这里,变量output将是ReadableStream,它与可读流中包含的数据不同。简而言之,就像文件系统中有文件一样,您可以获得有关文件的各种系统信息,但是文件中包含的内容是通过不同的调用来访问的。