Analyse d'un fichier CSV à l'aide de NodeJS

125

Avec nodejs, je veux analyser un fichier .csv de 10000 enregistrements et effectuer une opération sur chaque ligne. J'ai essayé d'utiliser http://www.adaltas.com/projects/node-csv . Je ne pouvais pas faire une pause à chaque ligne. Cela lit simplement tous les 10000 enregistrements. Je dois faire ce qui suit:

  1. lire le csv ligne par ligne
  2. effectuer une opération chronophage sur chaque ligne
  3. aller à la ligne suivante

Quelqu'un peut-il suggérer des idées alternatives ici?

solitaire
la source
Peut-être que cela vous aidera: stackoverflow.com/a/15554600/1169798
Sirko
1
Avez-vous ajouté des rappels pour chaque ligne? Sinon, il les lira tous de manière asynchrone.
Ben Fortune

Réponses:

81

Il semble que vous deviez utiliser une solution basée sur le flux, il existait déjà de telles bibliothèques, alors avant de vous réinventer, essayez cette bibliothèque, qui comprend également la prise en charge de la validation. https://www.npmjs.org/package/fast-csv

Risto Novik
la source
27
NodeCSV est également bien pris en charge et compte environ un ordre de grandeur supplémentaire d'utilisateurs. npmjs.com/package/csv
steampowered
4
fast-csv est rapide, facile à utiliser et à démarrer.
Roger Garzon Nieto
1
Prend-il en charge l'URL?
DMS-KH
57

J'ai utilisé de cette façon: -

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);
    });
vigne
la source
2
Je fais peut-être quelque chose de mal, mais quand je lance ceci, ce parsen'est pas défini. Y a-t-il quelque chose qui me manque? Quand je cours npm install csv-parse, puis dans mon code ajouter var parse = require("csv-parse");, cela fonctionne. Êtes-vous sûr que le vôtre fonctionne? Quoi qu'il en soit, j'adore cette solution (même si je dois inclure le csv-parsemodule
Ian
1
vous avez raison @lan, cela devrait être un csv-parsemodule d' inclusion .
vigne
1
Génial, merci d'avoir vérifié et mis à jour votre réponse!
Ian le
3
Belle solution. Travaille pour moi.
Sun Bee
3
malheureusement c'est mauvais - j'ai eu des erreurs avec des fichiers énormes et de longues lignes .... (erreurs de mémoire - bien que d'autres façons de le lire - fonctionne)
Seti
55

Ma solution actuelle utilise le module async pour s'exécuter en 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
la source
1
Je pense que vous en manquez ')'?
Steven Luong C du
Je pense que l'ajout d'un ')' à la fin des lignes 14 et 15 devrait résoudre le problème.
Jon
@ShashankVivek - dans cette ancienne réponse (de 2015), 'async' est une bibliothèque npm qui est utilisée. Plus à ce sujet ici caolan.github.io/async - pour comprendre pourquoi cela aide peut-être blog.risingstack.com/node-hero-async-programming-in-node-js Mais javascript a beaucoup évolué depuis 2015, et si votre question est plus sur l'async en général, alors lisez cet article plus récent medium.com/@tkssharma/…
prule
15
  • Cette solution utilise csv-parserau lieu d'être csv-parseutilisée dans certaines des réponses ci-dessus.
  • csv-parserest venu environ 2 ans après csv-parse.
  • Les deux résolvent le même objectif, mais personnellement, j'ai trouvé csv-parsermieux, car il est facile de gérer les en-têtes.

Installez d'abord le csv-parser:

npm install csv-parser

Supposons donc que vous ayez un fichier csv comme celui-ci:

NAME, AGE
Lionel Messi, 31
Andres Iniesta, 34

Vous pouvez effectuer l'opération requise comme suit:

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

Pour plus d'informations, reportez-vous

Pranche Tiwari
la source
13

Afin de mettre en pause le streaming dans fast-csv, vous pouvez faire ce qui suit:

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
la source
csvstream.pause () et resume () est ce que je cherchais! Mes applications manqueraient toujours de mémoire car elles lisaient les données beaucoup plus rapidement que ce qu'elles pouvaient traiter.
ehrhardt
@adnan Merci d'avoir signalé cela. Ce n'est pas mentionné dans la documentation et c'est ce que je recherchais également.
Piyush Beli
10

Le projet node-csv que vous référencez est tout à fait suffisant pour la tâche de transformation de chaque ligne d'une grande partie des données CSV, à partir de la documentation à l' adresse : 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());
    });
});

D'après mon expérience, je peux dire que c'est aussi une implémentation assez rapide, j'ai travaillé avec elle sur des ensembles de données avec près de 10k enregistrements et les temps de traitement étaient à un niveau raisonnable de dizaines de millisecondes pour l'ensemble.

Réorganisation de la suggestion de solution basée sur le flux de jurka : node-csv est basé sur le flux et suit l'API de streaming de Node.js.

Krwck
la source
8

Le module npm fast-csv peut lire les données ligne par ligne à partir du fichier csv.

Voici un exemple:

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
la source
1
[email protected] n'a pas fromStream()et son site de projet manque d'exemples et de documentation.
Cees Timmerman le
3

J'avais besoin d'un lecteur csv asynchrone et j'ai initialement essayé la réponse de @Pransh Tiwari mais je n'ai pas pu la faire fonctionner avec awaitet util.promisify(). Finalement, je suis tombé sur node-csvtojson , qui fait à peu près la même chose que csv-parser, mais avec des promesses. Voici un exemple d'utilisation de csvtojson en action:

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
la source
2

Essayez le plugin npm ligne par ligne.

npm install line-by-line --save
Nickast
la source
5
L'installation d'un plugin n'était pas la question qui se posait. Ajouter du code pour expliquer comment utiliser le plugin et / ou expliquer pourquoi l'OP devrait l'utiliser serait beaucoup plus avantageux.
domdambrogia
2

c'est ma solution pour obtenir un fichier csv à partir d'une URL externe

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
la source
2

Solution de contournement pour effectuer cette tâche avec await / async :

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

Ok donc il y a beaucoup de réponses ici et je ne pense pas qu'elles répondent à votre question qui, je pense, est similaire à la mienne.

Vous devez effectuer une opération comme contacter une base de données ou une API tierce qui prendra du temps et est asyncronus. Vous ne voulez pas charger le document entier en mémoire parce qu'il est trop volumineux ou pour une autre raison, vous devez donc lire ligne par ligne pour traiter.

J'ai lu les documents fs et cela peut faire une pause lors de la lecture, mais l'utilisation de l'appel .on ('data') rendra continuelle l'utilisation de la plupart de ces réponses et causera le problème.


MISE À JOUR: Je connais plus d'informations sur Streams que je ne l'aurais jamais voulu

La meilleure façon de procéder est de créer un flux inscriptible. Cela dirigera les données csv dans votre flux inscriptible que vous pourrez gérer les appels asyncronus. Le tube gérera le tampon jusqu'au lecteur afin que vous ne vous retrouviez pas avec une utilisation intensive de la mémoire

Version simple

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)

Version de la 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')
})

ANCIENNE MÉTHODE:

PROBLÈME AVEC lisible

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

Vous remarquerez un readingdrapeau. J'ai remarqué que pour une raison quelconque, juste à la fin du fichier, le .on («lisible») est appelé une deuxième fois sur des fichiers petits et grands. Je ne sais pas pourquoi, mais cela bloque cela à partir d'un deuxième processus de lecture des mêmes éléments de campagne.

BrinkDaDrink
la source
1

J'utilise celui-ci simple: https://www.npmjs.com/package/csv-parser

Très simple à utiliser:

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
la source
1

J'utilisais csv-parsemais pour les fichiers plus volumineux, il y avait des problèmes de performances, l'une des meilleures bibliothèques que j'ai trouvées est Papa Parse , les documents sont bons, un bon support, léger, pas de dépendances.

Installer papaparse

npm install papaparse

Usage:

  • asynchroniser / attendre
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()
  • rappeler
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.'); 
  }
});

La note header: trueest une option sur la configuration, voir la documentation pour d'autres options

Glen Thompson
la source
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
la source
0

Vous pouvez convertir csv au format json à l'aide du module csv-to-json, puis vous pouvez facilement utiliser le fichier json dans votre programme

Anuj Kumar
la source
-1

npm installer csv

Exemple de fichier CSV Vous allez avoir besoin d'un fichier CSV à analyser, donc soit vous en avez déjà un, soit vous pouvez copier le texte ci-dessous et le coller dans un nouveau fichier et appeler ce fichier "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

Exemple de lecture et d'analyse de code du fichier CSV

Créez un nouveau fichier et insérez-y le code suivant. Assurez-vous de lire ce qui se passe dans les coulisses.

    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.

Démarrez votre application et vérifiez la fonctionnalité Ouvrez une console et tapez la commande suivante:

Node app 1 Node app Vous devriez voir la sortie suivante dans votre 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'},] Vous devez maintenant ouvrir un navigateur Web et accéder à votre serveur. Vous devriez le voir afficher les données au format JSON.

Conclusion En utilisant node.js et son module CSV, nous pouvons rapidement et facilement lire et utiliser les données stockées sur le serveur et les mettre à disposition du client sur demande

Rubin Bhandari
la source