Analisando um arquivo CSV usando NodeJS

125

Com nodejs, quero analisar um arquivo .csv de 10.000 registros e fazer algumas operações em cada linha. Tentei usar http://www.adaltas.com/projects/node-csv . Eu não conseguia fazer uma pausa em cada linha. Isso apenas lê todos os 10.000 registros. Eu preciso fazer o seguinte:

  1. ler csv linha por linha
  2. realizar operação demorada em cada linha
  3. vá para a próxima linha

Alguém pode sugerir alguma ideia alternativa aqui?

solidão
fonte
Talvez isso ajude você: stackoverflow.com/a/15554600/1169798
Sirko
1
Você adicionou callbacks para cada linha? Do contrário, ele apenas os lerá de forma assíncrona.
Ben Fortune

Respostas:

81

Parece que você precisa usar alguma solução baseada em fluxo, já existiam tais bibliotecas, então antes de se reinventar, experimente esta biblioteca, que também inclui suporte para validação. https://www.npmjs.org/package/fast-csv

Risto Novik
fonte
27
O NodeCSV também é bem suportado e tem aproximadamente uma ordem de magnitude a mais de usuários. npmjs.com/package/csv
steampowered em
4
fast-csv é rápido, fácil de usar e começar.
Roger Garzon Nieto
1
Suporta com url?
DMS-KH
57

Eu usei desta forma: -

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);
    });
vinha
fonte
2
Posso estar fazendo algo errado, mas quando executo isso, parsenão está definido. Tem algo que estou perdendo? Quando eu executo npm install csv-parsee, em seguida, no meu código add var parse = require("csv-parse");, então funciona. Tem certeza que o seu funciona? De qualquer forma, adoro esta solução (mesmo que tenha de incluir o csv-parsemódulo
Ian
1
você está certo @lan, deve ser um csv-parsemódulo de inclusão .
vineet
1
Incrível, obrigado por verificar e atualizar sua resposta!
Ian de
3
Ótima solução. Funciona para mim.
Sun Bee
3
infelizmente isso é ruim - eu tenho erros com arquivos enormes e longas linhas ... (erros de memória - embora outras formas de ler - funcionem)
Seti
55

Minha solução atual usa o módulo assíncrono para executar em série:

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);
prule
fonte
1
Eu acho que você sente falta de alguns ')'?
Steven Luong C
Acho que adicionar um ')' ao final das linhas 14 e 15 deve resolver o problema.
Jon
@ShashankVivek - nesta resposta antiga (de 2015), 'async' é uma biblioteca npm que é usada. Mais sobre isso aqui caolan.github.io/async - para entender por que talvez isso ajude blog.risingstack.com/node-hero-async-programming-in-node-js Mas o javascript evoluiu muito desde 2015, e se a sua dúvida é mais sobre assíncrono em geral, então leia este artigo mais recente medium.com/@tkssharma/…
prule
15
  • Esta solução usa em csv-parservez de csv-parseusar em algumas das respostas acima.
  • csv-parserveio cerca de 2 anos depois csv-parse.
  • Ambos resolvem o mesmo propósito, mas pessoalmente achei csv-parsermelhor, pois é fácil lidar com cabeçalhos por meio dele.

Instale o csv-parser primeiro:

npm install csv-parser

Suponha que você tenha um arquivo csv como este:

NAME, AGE
Lionel Messi, 31
Andres Iniesta, 34

Você pode realizar a operação necessária como:

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
});  

Para mais informações, consulte

Pransh Tiwari
fonte
13

Para pausar o streaming em csv rápido, você pode fazer o seguinte:

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)
    });
Adnan Kamili
fonte
csvstream.pause () e resume () é o que estou procurando! Meus aplicativos sempre ficavam sem memória porque liam os dados muito mais rápido do que podiam processar.
Ehrhardt
@adnan Obrigado por apontar isso. Não é mencionado na documentação e é isso que eu também estava procurando.
Piyush Beli
10

O projeto node-csv ao qual você está se referindo é completamente suficiente para a tarefa de transformar cada linha de uma grande parte dos dados CSV, a partir dos documentos em: 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());
    });
});

Pela minha experiência, posso dizer que também é uma implementação bastante rápida, tenho trabalhado com ela em conjuntos de dados com quase 10 mil registros e os tempos de processamento estavam em um nível razoável de dezenas de milissegundos para todo o conjunto.

Reinstalando a sugestão de solução baseada em stream de jurka : Node-csv IS stream baseado e segue a API de streaming Node.js.

krwck
fonte
8

O csv rápido módulo npm pode ler dados linha por linha do arquivo csv.

Aqui está um exemplo:

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");
 });
ramachandrareddy reddam
fonte
1
[email protected] não tem fromStream()e seu site de projeto carece de exemplos e documentação.
Cees Timmerman
3

Eu precisava de um leitor csv assíncrono e tentei originalmente a resposta de @Pransh Tiwari, mas não consegui fazê-lo funcionar com awaite util.promisify(). Eventualmente, encontrei node-csvtojson , que faz praticamente o mesmo que csv-parser, mas com promessas. Aqui está um exemplo de uso de csvtojson em ação:

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);
    });
};
alexkb
fonte
2

Experimente o plugin npm linha por linha.

npm install line-by-line --save
nickast
fonte
5
Instalar um plugin não foi a pergunta que foi feita. Adicionar algum código para explicar como usar o plugin e / ou explicar por que o OP deve usá-lo seria muito mais benéfico.
domdambrogia
2

esta é a minha solução para obter o arquivo csv da url externa

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');
Andrea Perdicchia
fonte
2

Solução alternativa para fazer esta tarefa com await / async :

const csv = require('csvtojson')
const csvFilePath = 'data.csv'
const array = await csv().fromFile(csvFilePath);
HMagdy
fonte
2

Ok, então há muitas respostas aqui e eu não acho que elas respondam à sua pergunta, que acho que é semelhante à minha.

Você precisa fazer uma operação como entrar em contato com um banco de dados ou uma API de terceiros que levará tempo e será assíncrona. Você não deseja carregar o documento inteiro na memória por ser muito grande ou por outro motivo, portanto, é necessário ler linha por linha para processar.

Eu li os documentos fs e ele pode fazer uma pausa na leitura, mas usar a chamada .on ('data') tornará contínuo o que a maioria dessas respostas usa e causa o problema.


ATUALIZAÇÃO: Sei mais informações sobre Streams do que jamais quis

A melhor maneira de fazer isso é criar um fluxo gravável. Isso irá canalizar os dados csv em seu fluxo gravável, que você pode gerenciar chamadas assíncronas. O pipe irá gerenciar o buffer de volta ao leitor para que você não termine com uso pesado de memória

Versão Simples

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)

Versão da classe

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')
})

MÉTODO ANTIGO:

PROBLEMA COM legível

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);
  });
}

Você notará uma readingbandeira. Percebi que, por algum motivo, perto do final do arquivo, o .on ('legível') é chamado uma segunda vez em arquivos pequenos e grandes. Não tenho certeza do motivo, mas isso bloqueia um segundo processo de leitura dos mesmos itens de linha.

BrinkDaDrink
fonte
1

Eu uso este simples: https://www.npmjs.com/package/csv-parser

Muito simples de usar:

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'])
  });
Xin
fonte
1

Eu estava usando, csv-parsemas para arquivos maiores estava tendo problemas de desempenho, uma das melhores bibliotecas que encontrei é Papa Parse , documentos são bons, bom suporte, leve, sem dependências.

Instalar papaparse

npm install papaparse

Uso:

  • assíncrono / esperar
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()
  • ligue de volta
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.'); 
  }
});

Nota header: trueé uma opção na configuração, veja a documentação para outras opções

Glen Thompson
fonte
0
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));
})
swapnil
fonte
0

Você pode converter csv para o formato json usando o módulo csv para json e então você pode facilmente usar o arquivo json em seu programa

Anuj Kumar
fonte
-1

npm install csv

Arquivo CSV de amostra Você precisará de um arquivo CSV para analisar, então você já tem um ou pode copiar o texto abaixo e colá-lo em um novo arquivo e chamar esse arquivo de "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

Amostra de leitura e análise de código do arquivo CSV

Crie um novo arquivo e insira o código a seguir nele. Certifique-se de ler o que está acontecendo nos bastidores.

    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.

Inicie seu aplicativo e verifique a funcionalidade Abra um console e digite o seguinte comando:

Node app 1 Node app Você deve ver a seguinte saída em seu console:

[  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'},] Agora você deve abrir um navegador da web e navegar até o seu servidor. Você deve ver a saída dos dados no formato JSON.

Conclusão Usando node.js e seu módulo CSV, podemos ler e usar dados armazenados no servidor de forma rápida e fácil e disponibilizá-los ao cliente mediante solicitação

Rubin Bhandari
fonte