Analisi di un file CSV utilizzando NodeJS


125

Con nodejs voglio analizzare un file .csv di 10000 record ed eseguire alcune operazioni su ogni riga. Ho provato a utilizzare http://www.adaltas.com/projects/node-csv . Non riuscivo a mettere questo in pausa ad ogni riga. Questo legge solo tutti i 10000 record. Devo fare quanto segue:

  1. leggi csv riga per riga
  2. eseguire operazioni che richiedono tempo su ciascuna linea
  3. vai alla riga successiva

Qualcuno può suggerire idee alternative qui?


Forse questo ti aiuterà: stackoverflow.com/a/15554600/1169798
Sirko

1
Hai aggiunto callback per ogni riga? Altrimenti li leggerà tutti in modo asincrono.
Ben Fortune

Risposte:


81

Sembra che tu abbia bisogno di usare una soluzione basata sul flusso, esistevano già tali librerie quindi prima di reinventarti, prova questa libreria, che include anche il supporto per la convalida. https://www.npmjs.org/package/fast-csv


27
NodeCSV è anche ben supportato e sembra avere circa un ordine di grandezza in più di utenti. npmjs.com/package/csv
steampowered il

4
fast-csv è veloce, facile da usare e da iniziare.
Roger Garzon Nieto

1
Supporta con l'URL?
DMS-KH,

57

Ho usato in questo modo: -

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

2
Potrei fare qualcosa di sbagliato, ma quando lo eseguo parsenon è definito. C'è qualcosa che mi manca? Quando eseguo npm install csv-parsee poi nel mio codice aggiungo var parse = require("csv-parse");, allora funziona. Sei sicuro che il tuo funzioni? Ad ogni modo, adoro questa soluzione (anche se devo includere il csv-parsemodulo
Ian

1
hai ragione @lan, dovrebbe essere include csv-parsemodule.
Vineet

1
Fantastico, grazie per aver verificato e aggiornato la tua risposta!
Ian

3
Bella soluzione. Per me va bene.
Sun Bee

3
purtroppo questo è un male - ho ricevuto errori con file enormi e lunghe file ... (errori di memoria - anche se altri modi di leggerlo - funzionano)
Seti

55

La mia soluzione attuale utilizza il modulo async per eseguire in serie:

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

1
Penso che ti manchi un po '') '?
Steven Luong C,

Penso che l'aggiunta di un ")" alla fine delle righe 14 e 15 dovrebbe risolvere il problema.
Jon

@ShashankVivek - in questa vecchia risposta (dal 2015), "async" è una libreria npm utilizzata. Maggiori informazioni qui caolan.github.io/async - per capire perché forse questo aiuta blog.risingstack.com/node-hero-async-programming-in-node-js Ma javascript si è evoluto molto dal 2015 e se la tua domanda è più sull'asincronia in generale, quindi leggi questo articolo più recente medium.com/@tkssharma/…
prule

15
  • Questa soluzione utilizza csv-parserinvece di essere csv-parseutilizzata in alcune delle risposte precedenti.
  • csv-parserè venuto circa 2 anni dopo csv-parse.
  • Entrambi risolvono lo stesso scopo, ma personalmente ho trovato di csv-parsermeglio, poiché è facile gestire le intestazioni attraverso di esso.

Installa prima il parser csv:

npm install csv-parser

Quindi supponi di avere un file csv come questo:

NAME, AGE
Lionel Messi, 31
Andres Iniesta, 34

È possibile eseguire l'operazione richiesta come:

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

Per ulteriori letture fare riferimento


13

Per mettere in pausa lo streaming in fast-csv puoi fare quanto segue:

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 () e resume () è quello che stavo cercando! Le mie applicazioni esaurirebbero sempre la memoria perché leggono i dati molto più velocemente di quanto potrebbe elaborare.
ehrhardt

@adnan Grazie per averlo sottolineato. Non è menzionato nella documentazione ed è quello che stavo cercando.
Piyush Beli

10

Il progetto node-csv a cui stai facendo riferimento è completamente sufficiente per il compito di trasformare ogni riga di una grande porzione di dati CSV, dalla documentazione su: 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());
    });
});

Dalla mia esperienza, posso dire che è anche un'implementazione piuttosto veloce, ci ho lavorato su set di dati con quasi 10.000 record e i tempi di elaborazione erano a un livello ragionevole di decine di millisecondi per l'intero set.

Rinnovare il suggerimento della soluzione basata sul flusso di jurka : node-csv IS basato sul flusso e segue l'API di streaming di Node.js.


8

Il modulo npm fast-csv può leggere i dati riga per riga dal file csv.

Ecco un esempio:

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

1
fast-csv@4.0.2 non ha fromStream()e il sito del progetto manca di esempi e documentazione.
Cees Timmerman,

3

Avevo bisogno di un lettore csv asincrono e inizialmente ho provato la risposta di @Pransh Tiwari ma non sono riuscito a farlo funzionare con awaite util.promisify(). Alla fine mi sono imbattuto in node-csvtojson , che praticamente fa lo stesso di csv-parser, ma con delle promesse. Ecco un esempio di utilizzo di csvtojson in azione:

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

2

Prova il plugin npm riga per riga.

npm install line-by-line --save

5
L'installazione di un plugin non era la domanda che veniva posta. Aggiungere del codice per spiegare come usare il plugin e / o spiegare perché l'OP dovrebbe usarlo sarebbe molto più vantaggioso.
domdambrogia

2

questa è la mia soluzione per ottenere il file CSV da un URL esterno

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

2

Soluzione alternativa per eseguire questa attività con await / async :

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

2

Ok, quindi ci sono molte risposte qui e non credo che rispondano alla tua domanda che penso sia simile alla mia.

È necessario eseguire un'operazione come contattare un database o un'API di terze parti che richiederà tempo ed è asincrono. Non si desidera caricare l'intero documento in memoria perché è troppo grande o per qualche altro motivo, quindi è necessario leggere riga per riga per l'elaborazione.

Ho letto i documenti fs e posso fare una pausa durante la lettura, ma l'uso della chiamata .on ('data') renderà continuo ciò che la maggior parte di queste risposte usa e causa il problema.


AGGIORNAMENTO: Conosco più informazioni sugli stream di quanto avrei mai voluto

Il modo migliore per farlo è creare uno stream scrivibile. Questo reindirizzerà i dati csv nel tuo flusso scrivibile che puoi gestire le chiamate asincrone. Il pipe gestirà il buffer fino al lettore in modo da non finire con un utilizzo intenso della memoria

Versione semplice

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)

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

VECCHIO METODO:

PROBLEMA CON leggibile

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

Noterai una readingbandiera. Ho notato che per qualche motivo proprio verso la fine del file il .on ('readable') viene chiamato una seconda volta su file piccoli e grandi. Non sono sicuro del perché, ma questo impedisce che un secondo processo legga gli stessi elementi pubblicitari.


1

Uso questo semplice: https://www.npmjs.com/package/csv-parser

Molto semplice da usare:

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

1

Stavo usando, csv-parsema per file più grandi si verificavano problemi di prestazioni una delle migliori librerie che ho trovato è Papa Parse , i documenti sono buoni, un buon supporto, leggero, senza dipendenze.

Installare papaparse

npm install papaparse

Uso:

  • async / attendere
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()
  • richiama
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è un'opzione nella configurazione, vedere la documentazione per altre opzioni


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

0

Puoi convertire csv in formato json usando il modulo csv-to-json e quindi puoi facilmente usare il file json nel tuo programma


-1

npm installa csv

File CSV di esempio Avrai bisogno di un file CSV da analizzare, quindi o ne hai già uno, oppure puoi copiare il testo qui sotto e incollarlo in un nuovo file e chiamare quel file "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

Esempio di lettura del codice e analisi del file CSV

Crea un nuovo file e inserisci il seguente codice al suo interno. Assicurati di leggere cosa sta succedendo dietro le quinte.

    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.

Avvia la tua app e verifica la funzionalità Apri una console e digita il seguente comando:

App nodo 1 App nodo Dovresti vedere il seguente output nella tua 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'},] Ora dovresti aprire un browser web e navigare sul tuo server. Dovresti vederlo restituire i dati in formato JSON.

Conclusione Utilizzando node.js e il suo modulo CSV possiamo leggere e utilizzare rapidamente e facilmente i dati memorizzati sul server e renderli disponibili al client su richiesta

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.