使用 NodeJS 解析 CSV 文件

Posted

技术标签:

【中文标题】使用 NodeJS 解析 CSV 文件【英文标题】:Parsing a CSV file using NodeJS [closed] 【发布时间】:2014-05-29 14:41:54 【问题描述】:

使用 nodejs,我想解析一个包含 10000 条记录的 .csv 文件,并对每一行进行一些操作。我尝试使用http://www.adaltas.com/projects/node-csv。我不能让它在每一行暂停。这只是读取所有 10000 条记录。我需要执行以下操作:

    逐行读取csv 在每条线上执行耗时的操作 转到下一行

任何人都可以在这里提出任何替代想法吗?

【问题讨论】:

也许对你有帮助:***.com/a/15554600/1169798 你是否为每一行添加了回调?否则它只会异步读取它们。 【参考方案1】:

您似乎需要使用基于流的库,例如 fast-csv,其中还包括验证支持。

【讨论】:

NodeCSV 也得到了很好的支持,并且恰好有大约一个数量级的用户。 npmjs.com/package/csv fast-csv 速度快、易于使用和上手。 是否支持带url?【参考方案2】:

我是这样用的:-

var fs = require('fs'); 
var parse = require('csv-parse');

var csvData=[];
fs.createReadStream(req.file.path)
    .pipe(parse(delimiter: ':'))
    .on('data', function(csvrow) 
        console.log(csvrow);
        //do something with csvrow
        csvData.push(csvrow);        
    )
    .on('end',function() 
      //do something with csvData
      console.log(csvData);
    );

【讨论】:

我可能做错了什么,但是当我运行它时,parse 没有定义。有什么我想念的吗?当我运行npm install csv-parse 然后在我的代码中添加var parse = require("csv-parse"); 时,它就可以工作了。你确定你的工作?无论哪种方式,我都喜欢这个解决方案(即使我必须包含 csv-parse 模块 你是对的@lan,应该是包含csv-parse模块。 不错的解决方案。为我工作。 很遗憾,这很糟糕 - 我遇到了大文件和长行的错误......(内存错误 - 尽管其他读取方式 - 有效) 遗憾的是,这会添加到数组中,但只能通过 .on('end') 访问,不能在此语句之外访问。我认为这是由于它是同步的。异步功能需要解释。【参考方案3】:

我目前的解决方案使用异步模块串行执行:

var fs = require('fs');
var parse = require('csv-parse');
var async = require('async');

var inputFile='myfile.csv';

var parser = parse(delimiter: ',', function (err, data) 
  async.eachSeries(data, function (line, callback) 
    // do something with the line
    doSomething(line).then(function() 
      // when processing finishes invoke the callback to move to the next one
      callback();
    );
  )
);
fs.createReadStream(inputFile).pipe(parser);

【讨论】:

我想你错过了一些')'? 我认为在第 14 行和第 15 行的末尾添加一个 ')' 应该可以解决问题。 @ShashankVivek - 在这个旧答案(从 2015 年开始)中,'async' 是一个使用的 npm 库。更多关于它的信息在这里caolan.github.io/async - 了解为什么这可能会有所帮助blog.risingstack.com/node-hero-async-programming-in-node-js 但是自 2015 年以来 javascript 已经发展了很多,如果您的问题更多的是关于一般异步,那么请阅读这篇更新的文章 medium.com/@tkssharma/…【参考方案4】: 这个解决方案使用csv-parser而不是csv-parse 上面的答案。 csv-parser 大约 2 年后出现 csv-parse。 它们都解决了相同的目的,但我个人发现 csv-parser 更好,因为它很容易通过它处理标题。

先安装 csv-parser:

npm install csv-parser

所以假设你有一个这样的 csv 文件:

NAME, AGE
Lionel Messi, 31
Andres Iniesta, 34

您可以按如下方式执行所需的操作:

const fs = require('fs'); 
const csv = require('csv-parser');

fs.createReadStream(inputFilePath)
.pipe(csv())
.on('data', function(data)
    try 
        console.log("Name is: "+data.NAME);
        console.log("Age is: "+data.AGE);

        //perform the operation
    
    catch(err) 
        //error handler
    
)
.on('end',function()
    //some final operation
);  

进一步阅读refer

【讨论】:

感谢分享。 csv-parser 的最大好处之一是包的大小。 csv-parser 约为 27KB,而 csv-parse 约为 1.6MB。【参考方案5】:

要暂停fast-csv 中的流式传输,您可以执行以下操作:

let csvstream = csv.fromPath(filePath,  headers: true )
    .on("data", function (row) 
        csvstream.pause();
        // do some heavy work
        // when done resume the stream
        csvstream.resume();
    )
    .on("end", function () 
        console.log("We are done!")
    )
    .on("error", function (error) 
        console.log(error)
    );

【讨论】:

csvstream.pause() 和 resume() 是我一直在寻找的!我的应用程序总是会耗尽内存,因为它读取数据的速度比它可以处理的速度快得多。 @adnan 感谢您指出这一点。文档中没有提到它,这也是我一直在寻找的。​​span> 【参考方案6】:

您引用的 node-csv 项目完全足以完成转换大量 CSV 数据的每一行的任务,来自以下文档:http://csv.adaltas.com/transform/:

csv()
  .from('82,Preisner,Zbigniew\n94,Gainsbourg,Serge')
  .to(console.log)
  .transform(function(row, index, callback)
    process.nextTick(function()
      callback(null, row.reverse());
    );
);

根据我的经验,我可以说这也是一个相当快的实现,我一直在使用它处理具有近 10k 条记录的数据集,整个数据集的处理时间在合理的几十毫秒级别.

阅读 jurka 的基于流的解决方案建议:node-csv 是基于流并遵循 Node.js 的流 API。

【讨论】:

【参考方案7】:

fast-csv npm 模块可以从 csv 文件中逐行读取数据。

这是一个例子:

let csv= require('fast-csv');

var stream = fs.createReadStream("my.csv");

csv
 .parseStream(stream, headers : true)
 .on("data", function(data)
     console.log('I am one line of data', data);
 )
 .on("end", function()
     console.log("done");
 );

【讨论】:

fast-csv@4.0.2 没有 fromStream() 并且其项目站点缺少示例和文档。【参考方案8】:

我需要一个异步 csv 阅读器,最初尝试了 @Pransh Tiwari 的答案,但无法使用 awaitutil.promisify()。最终我遇到了node-csvtojson,它与 csv-parser 的功能几乎相同,但带有承诺。以下是 csvtojson 的实际使用示例:

const csvToJson = require('csvtojson');

const processRecipients = async () => 
    const recipients = await csvToJson(
        trim:true
    ).fromFile('./recipients.csv');

    // Code executes after recipients are fully loaded.
    recipients.forEach((recipient) => 
        console.log(recipient.name, recipient.email);
    );
;

【讨论】:

【参考方案9】:

好的,所以这里有很多答案,我认为他们无法回答您的问题,我认为这些问题与我的相似。

您需要执行一个操作,例如联系数据库或第三方 api,这需要时间并且是异步的。由于文件太大或其他原因,您不想将整个文档加载到内存中,因此您需要逐行读取以进行处理。

我已阅读 fs 文档,它可以在阅读时暂停,但使用 .on('data') 调用将使其连续,其中大多数答案使用并导致问题。


更新:我知道的有关 Streams 的信息比我想的要多

最好的方法是创建一个可写流。这会将 csv 数据通过管道传输到您可以管理异步调用的可写流中。管道将管理缓冲区一直返回到阅读器,因此您不会因大量内存使用而告终

简单版

const parser = require('csv-parser');
const stripBom = require('strip-bom-stream');
const stream = require('stream')

const mySimpleWritable = new stream.Writable(
  objectMode: true, // Because input is object from csv-parser
  write(chunk, encoding, done)  // Required
    // chunk is object with data from a line in the csv
    console.log('chunk', chunk)
    done();
  ,
  final(done)  // Optional
    // last place to clean up when done
    done();
  
);
fs.createReadStream(fileNameFull).pipe(stripBom()).pipe(parser()).pipe(mySimpleWritable)

类版本

const parser = require('csv-parser');
const stripBom = require('strip-bom-stream');
const stream = require('stream')
// Create writable class
class MyWritable extends stream.Writable 
  // Used to set object mode because we get an object piped in from csv-parser
  constructor(another_variable, options) 
    // Calls the stream.Writable() constructor.
    super( ...options, objectMode: true );
    // additional information if you want
    this.another_variable = another_variable
  
  // The write method
  // Called over and over, for each line in the csv
  async _write(chunk, encoding, done) 
    // The chunk will be a line of your csv as an object
    console.log('Chunk Data', this.another_variable, chunk)

    // demonstrate await call
    // This will pause the process until it is finished
    await new Promise(resolve => setTimeout(resolve, 2000));

    // Very important to add.  Keeps the pipe buffers correct.  Will load the next line of data
    done();
  ;
  // Gets called when all lines have been read
  async _final(done) 
    // Can do more calls here with left over information in the class
    console.log('clean up')
    // lets pipe know its done and the .on('final') will be called
    done()
  


// Instantiate the new writable class
myWritable = new MyWritable(somevariable)
// Pipe the read stream to csv-parser, then to your write class
// stripBom is due to Excel saving csv files with UTF8 - BOM format
fs.createReadStream(fileNameFull).pipe(stripBom()).pipe(parser()).pipe(myWritable)

// optional
.on('finish', () => 
  // will be called after the wriables internal _final
  console.log('Called very last')
)

旧方法:

可读性问题

const csv = require('csv-parser');
const fs = require('fs');

const processFileByLine = async(fileNameFull) => 

  let reading = false

  const rr = fs.createReadStream(fileNameFull)
  .pipe(csv())

  // Magic happens here
  rr.on('readable', async function()
    // Called once when data starts flowing
    console.log('starting readable')

    // Found this might be called a second time for some reason
    // This will stop that event from happening
    if (reading) 
      console.log('ignoring reading')
      return
    
    reading = true
    
    while (null !== (data = rr.read())) 
      // data variable will be an object with information from the line it read
      // PROCESS DATA HERE
      console.log('new line of data', data)
    

    // All lines have been read and file is done.
    // End event will be called about now so that code will run before below code

    console.log('Finished readable')
  )


  rr.on("end", function () 
    // File has finished being read
    console.log('closing file')
  );

  rr.on("error", err => 
    // Some basic error handling for fs error events
    console.log('error', err);
  );

您会注意到reading 标志。我注意到,由于某种原因,在文件末尾附近, .on('readable') 在大小文件上被第二次调用。我不确定为什么,但这会阻止第二个进程读取相同的行项目。

【讨论】:

【参考方案10】:

我用这个简单的:https://www.npmjs.com/package/csv-parser

使用非常简单:

const csv = require('csv-parser')
const fs = require('fs')
const results = [];

fs.createReadStream('./CSVs/Update 20191103C.csv')
  .pipe(csv())
  .on('data', (data) => results.push(data))
  .on('end', () => 
    console.log(results);
    console.log(results[0]['Lowest Selling Price'])
  );

【讨论】:

【参考方案11】:
var fs = require("fs");
// READ CSV INTO STRING
var data = fs.readFileSync("your.csv").toLocaleString();

// STRING TO ARRAY
var rows = data.split("\n"); // SPLIT ROWS
rows.forEach((row) => 
    columns = row.split(","); //SPLIT COLUMNS
    console.log(columns);
)

【讨论】:

将整个文件读入内存通常是个坏主意,事后拆分更糟糕;现在你在内存中的文件大小增加了一倍。【参考方案12】:

逐行尝试npm插件。

npm install line-by-line --save

【讨论】:

安装插件不是被问到的问题。添加一些代码来解释如何使用插件和/或解释为什么 OP 应该使用它会更有益。【参考方案13】:

这是我从外部 url 获取 csv 文件的解决方案

const parse = require( 'csv-parse/lib/sync' );
const axios = require( 'axios' );
const readCSV = ( module.exports.readCSV = async ( path ) => 
try 
   const res = await axios(  url: path, method: 'GET', responseType: 'blob'  );
   let records = parse( res.data, 
      columns: true,
      skip_empty_lines: true
     );

    return records;
  catch ( e ) 
   console.log( 'err' );
 

 );
readCSV('https://urltofilecsv');

【讨论】:

【参考方案14】:

我使用的是csv-parse,但是对于较大的文件会遇到性能问题,我发现的更好的库之一是Papa Parse,文档很好,支持良好,轻量级,没有依赖关系。

安装papaparse

npm install papaparse

用法:

异步/等待
const fs = require('fs');
const Papa = require('papaparse');

const csvFilePath = 'data/test.csv'

// Function to read csv which returns a promise so you can do async / await.

const readCSV = async (filePath) => 
  const csvFile = fs.readFileSync(filePath)
  const csvData = csvFile.toString()  
  return new Promise(resolve => 
    Papa.parse(csvData, 
      header: true,
      transformHeader: header => header.trim(),
      complete: results => 
        console.log('Complete', results.data.length, 'records.'); 
        resolve(results.data);
      
    );
  );
;

const test = async () => 
  let parsedData = await readCSV(csvFilePath); 


test()
回调
const fs = require('fs');
const Papa = require('papaparse');

const csvFilePath = 'data/test.csv'

const file = fs.createReadStream(csvFilePath);

var csvData=[];
Papa.parse(file, 
  header: true,
  transformHeader: header => header.trim(),
  step: function(result) 
    csvData.push(result.data)
  ,
  complete: function(results, file) 
    console.log('Complete', csvData.length, 'records.'); 
  
);

注意header: true 是配置中的一个选项,请参阅文档了解其他选项

【讨论】:

【参考方案15】:
fs = require('fs');
fs.readFile('FILENAME WITH PATH','utf8', function(err,content)
if(err)
    console.log('error occured ' +JSON.stringify(err));
 
 console.log('Fileconetent are ' + JSON.stringify(content));
)

【讨论】:

【参考方案16】:

您可以使用 csv-to-json 模块将 csv 转换为 json 格式,然后您可以轻松地在程序中使用 json 文件

【讨论】:

【参考方案17】:

我已经使用 Promise 方法完成了这项工作

const fs = require('fs')
const parse = require('csv-parse')
function readFile(path)
    return new Promise((resolve,reject)=>
        fs.readFile(path, function (err, fileData) 
            parse(fileData, columns: false, trim: true, async function(err, rows) 
                if(err)
                    reject(err)
                
                resolve(rows)
            )
          )
    )

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案18】:

npm 安装 csv

CSV 文件示例 您将需要一个 CSV 文件来解析,因此您已经有一个,或者您可以复制下面的文本并将其粘贴到一个新文件中,然后将该文件称为“mycsv.csv”

ABC, 123, Fudge
532, CWE, ICECREAM
8023, POOP, DOGS
441, CHEESE, CARMEL
221, ABC, HOUSE
1
ABC, 123, Fudge
2
532, CWE, ICECREAM
3
8023, POOP, DOGS
4
441, CHEESE, CARMEL
5
221, ABC, HOUSE

读取和解析 CSV 文件的示例代码

创建一个新文件,并将以下代码插入其中。请务必阅读幕后发生的事情。

    var csv = require('csv'); 
    // loads the csv module referenced above.

    var obj = csv(); 
    // gets the csv module to access the required functionality

    function MyCSV(Fone, Ftwo, Fthree) 
        this.FieldOne = Fone;
        this.FieldTwo = Ftwo;
        this.FieldThree = Fthree;
    ; 
    // Define the MyCSV object with parameterized constructor, this will be used for storing the data read from the csv into an array of MyCSV. You will need to define each field as shown above.

    var MyData = []; 
    // MyData array will contain the data from the CSV file and it will be sent to the clients request over HTTP. 

    obj.from.path('../THEPATHINYOURPROJECT/TOTHE/csv_FILE_YOU_WANT_TO_LOAD.csv').to.array(function (data) 
        for (var index = 0; index < data.length; index++) 
            MyData.push(new MyCSV(data[index][0], data[index][1], data[index][2]));
        
        console.log(MyData);
    );
    //Reads the CSV file from the path you specify, and the data is stored in the array we specified using callback function.  This function iterates through an array and each line from the CSV file will be pushed as a record to another array called MyData , and logs the data into the console to ensure it worked.

var http = require('http');
//Load the http module.

var server = http.createServer(function (req, resp) 
    resp.writeHead(200,  'content-type': 'application/json' );
    resp.end(JSON.stringify(MyData));
);
// Create a webserver with a request listener callback.  This will write the response header with the content type as json, and end the response by sending the MyData array in JSON format.

server.listen(8080);
// Tells the webserver to listen on port 8080(obviously this may be whatever port you want.)
1
var csv = require('csv'); 
2
// loads the csv module referenced above.
3
​
4
var obj = csv(); 
5
// gets the csv module to access the required functionality
6
​
7
function MyCSV(Fone, Ftwo, Fthree) 
8
    this.FieldOne = Fone;
9
    this.FieldTwo = Ftwo;
10
    this.FieldThree = Fthree;
11
; 
12
// Define the MyCSV object with parameterized constructor, this will be used for storing the data read from the csv into an array of MyCSV. You will need to define each field as shown above.
13
​
14
var MyData = []; 
15
// MyData array will contain the data from the CSV file and it will be sent to the clients request over HTTP. 
16
​
17
obj.from.path('../THEPATHINYOURPROJECT/TOTHE/csv_FILE_YOU_WANT_TO_LOAD.csv').to.array(function (data) 
18
    for (var index = 0; index < data.length; index++) 
19
        MyData.push(new MyCSV(data[index][0], data[index][1], data[index][2]));
20
    
21
    console.log(MyData);
22
);
23
//Reads the CSV file from the path you specify, and the data is stored in the array we specified using callback function.  This function iterates through an array and each line from the CSV file will be pushed as a record to another array called MyData , and logs the data into the console to ensure it worked.
24
​
25
var http = require('http');
26
//Load the http module.
27
​
28
var server = http.createServer(function (req, resp) 
29
    resp.writeHead(200,  'content-type': 'application/json' );
30
    resp.end(JSON.stringify(MyData));
31
);
32
// Create a webserver with a request listener callback.  This will write the response header with the content type as json, and end the response by sending the MyData array in JSON format.
33
​
34
server.listen(8080);
35
// Tells the webserver to listen on port 8080(obviously this may be whatever port you want.)
Things to be aware of in your app.js code
In lines 7 through 11, we define the function called 'MyCSV' and the field names.

If your CSV file has multiple columns make sure you define this correctly to match your file.

On line 17 we define the location of the CSV file of which we are loading.  Make sure you use the correct path here.

启动您的应用并验证功能 打开控制台并输入以下命令:

节点应用 1 节点应用 您应该在控制台中看到以下输出:

[  MYCSV  Fieldone: 'ABC', Fieldtwo: '123', Fieldthree: 'Fudge' ,
   MYCSV  Fieldone: '532', Fieldtwo: 'CWE', Fieldthree: 'ICECREAM' ,
   MYCSV  Fieldone: '8023', Fieldtwo: 'POOP', Fieldthree: 'DOGS' ,
   MYCSV  Fieldone: '441', Fieldtwo: 'CHEESE', Fieldthree: 'CARMEL' ,
   MYCSV  Fieldone: '221', Fieldtwo: 'ABC', Fieldthree: 'HOUSE' , ]

1 [ MYCSV Fieldone:'ABC',Fieldtwo:'123',Fieldthree:'Fudge', 2 MYCSV Fieldone:'532',Fieldtwo:'CWE',Fieldthree:'ICECREAM', 3 MYCSV Fieldone:'8023',Fieldtwo:'POOP',Fieldthree:'DOGS', 4 MYCSV Fieldone:'441',Fieldtwo:'CHEESE',Fieldthree:'CARMEL', 5 MYCSV Fieldone:'221',Fieldtwo:'ABC',Fieldthree:'HOUSE',] 现在您应该打开一个网络浏览器并导航到您的服务器。您应该会看到它以 JSON 格式输出数据。

结论 使用 node.js 和它的 CSV 模块,我们可以快速轻松地读取和使用存储在服务器上的数据,并根据请求将其提供给客户端

【讨论】:

以上是关于使用 NodeJS 解析 CSV 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 json2csv nodejs 模块将 JSON 对象解析为 CSV 文件

NodeJS:读取一个大的 csv 文件

使用 json2csv nodejs 的 JSON 到 CSV 文件

如何使用 angular 和 nodejs 将 csv 数据转储到 mongoDB

Node js:以编程方式更改 csv 文件编码并解析为 json

在 NodeJS 中将 200'000 行以上的大型 csv 文件插入 MongoDB