Analizando un archivo CSV usando NodeJS

125

Con nodejs quiero analizar un archivo .csv de 10000 registros y hacer alguna operación en cada fila. Intenté usar http://www.adaltas.com/projects/node-csv . No pude hacer que esto se detuviera en cada fila. Esto solo lee todos los 10000 registros. Necesito hacer lo siguiente:

  1. leer csv línea por línea
  2. realizar operaciones que requieren mucho tiempo en cada línea
  3. ir a la siguiente línea

¿Alguien puede sugerir alguna idea alternativa aquí?

solitario
fuente
Tal vez eso te ayude: stackoverflow.com/a/15554600/1169798
Sirko
1
¿Agregó devoluciones de llamada para cada fila? De lo contrario, solo los leerá todos de forma asincrónica.
Ben Fortune

Respuestas:

81

Parece que necesita usar alguna solución basada en flujo, ya existían tales bibliotecas, así que antes de reinventarse, pruebe esta biblioteca, que también incluye soporte de validación. https://www.npmjs.org/package/fast-csv

Risto Novik
fuente
27
NodeCSV también es compatible y tiene aproximadamente un orden de magnitud más de usuarios. npmjs.com/package/csv
steampowered
4
fast-csv es rápido, fácil de usar y empezar.
Roger Garzon Nieto
1
¿Es compatible con url?
DMS-KH
57

Usé de esta manera: -

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);
    });
vineet
fuente
2
Puede que esté haciendo algo mal, pero cuando ejecuto esto, parseno está definido. ¿Se me escapa algo? Cuando ejecuto npm install csv-parsey luego en mi código agrego var parse = require("csv-parse");, entonces funciona. ¿Estás seguro de que el tuyo funciona? De cualquier manera, me encanta esta solución (incluso si tengo que incluir el csv-parsemódulo
Ian
1
tienes razón @lan, debería incluir el csv-parsemódulo.
vineet
1
Genial, ¡gracias por verificar y actualizar tu respuesta!
Ian
3
Buena solucion. Funciona para mi.
Sun Bee
3
Lamentablemente, esto es malo: obtuve errores con archivos enormes y largas filas ... (errores de memoria, aunque otras formas de leerlo, funcionan)
Seti
55

Mi solución actual usa el módulo async para ejecutar en 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);
prule
fuente
1
Creo que echas de menos algunos ')'?
Steven Luong C
Creo que agregar un ')' al final de las líneas 14 y 15 debería solucionar el problema.
Jon
@ShashankVivek: en esta respuesta anterior (de 2015), 'async' es una biblioteca npm que se usa. Más sobre esto aquí caolan.github.io/async : para comprender por qué tal vez esto ayude a blog.risingstack.com/node-hero-async-programming-in-node-js Pero javascript ha evolucionado mucho desde 2015, y si su pregunta trata más sobre async en general, entonces lea este artículo más reciente medium.com/@tkssharma/…
prule
15
  • Esta solución se usa en csv-parserlugar de csv-parseusarse en algunas de las respuestas anteriores.
  • csv-parservino alrededor de 2 años después csv-parse.
  • Ambos resuelven el mismo propósito, pero personalmente me ha parecido csv-parsermejor, ya que es fácil manejar encabezados a través de él.

Instale el csv-parser primero:

npm install csv-parser

Entonces, suponga que tiene un archivo csv como este:

NAME, AGE
Lionel Messi, 31
Andres Iniesta, 34

Puede realizar la operación requerida 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 obtener más información, consulte

Pransh Tiwari
fuente
13

Para pausar la transmisión en fast-csv , puede hacer lo siguiente:

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
fuente
¡csvstream.pause () y resume () es lo que estaba buscando! Mis aplicaciones siempre se quedarían sin memoria porque leían datos mucho más rápido de lo que podían procesar.
ehrhardt
@adnan Gracias por señalar esto. No se menciona en la documentación y eso es lo que también estaba buscando.
Piyush Beli
10

El proyecto node-csv al que hace referencia es completamente suficiente para la tarea de transformar cada fila de una gran parte de los datos CSV, de los documentos en: 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());
    });
});

Desde mi experiencia, puedo decir que también es una implementación bastante rápida, he estado trabajando con ella en conjuntos de datos con cerca de 10k registros y los tiempos de procesamiento estaban en un nivel razonable de decenas de milisegundos para todo el conjunto.

Con respecto a la sugerencia de solución basada en flujo de jurka : node-csv IS basado en flujo y sigue la API de transmisión de Node.js.

krwck
fuente
8

El módulo npm fast-csv puede leer datos línea por línea desde el archivo csv.

Aquí hay un ejemplo:

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
fuente
1
[email protected] no tiene fromStream()y su sitio de proyecto carece de ejemplos y documentación.
Cees Timmerman
3

Necesitaba un lector csv asíncrono y originalmente probé la respuesta de @Pransh Tiwari, pero no pude hacer que funcionara con awaity util.promisify(). Finalmente me encontré con node-csvtojson , que prácticamente hace lo mismo que csv-parser, pero con promesas. Aquí hay un ejemplo de uso de csvtojson en acción:

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
fuente
2

Pruebe el complemento npm línea por línea.

npm install line-by-line --save
Nickast
fuente
5
Instalar un complemento no fue la pregunta que se hizo. Agregar algún código para explicar cómo usar el complemento y / o explicar por qué el OP debería usarlo sería mucho más beneficioso.
domdambrogia
2

esta es mi solución para obtener el archivo csv de la 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
fuente
2

Solución alternativa para realizar esta tarea con await / async :

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

Ok, hay muchas respuestas aquí y no creo que respondan a tu pregunta, que creo que es similar a la mía.

Debe realizar una operación como ponerse en contacto con una base de datos o una API de terceros que llevará tiempo y es asincrónica. No desea cargar todo el documento en la memoria debido a que es demasiado grande o por alguna otra razón, por lo que debe leer línea por línea para procesar.

He leído los documentos fs y puede pausar en la lectura, pero el uso de la llamada .on ('data') hará que sea continuo cuál de estas respuestas usa y causa el problema.


ACTUALIZACIÓN: Sé más información sobre Streams de la que nunca quise

La mejor forma de hacer esto es crear un flujo de escritura. Esto canalizará los datos csv a su flujo de escritura que puede administrar las llamadas asyncronus. La tubería administrará el búfer hasta el lector para que no termine con un uso intensivo de la memoria

Versión 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)

Versión de clase

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 ANTIGUO:

PROBLEMA CON legible

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

Notarás una readingbandera. He notado que, por alguna razón, cerca del final del archivo, el .on ('legible') se llama por segunda vez en archivos pequeños y grandes. No estoy seguro de por qué, pero esto bloquea eso de un segundo proceso que lee las mismas líneas de pedido.

BrinkDaDrink
fuente
1

Yo uso este simple: https://www.npmjs.com/package/csv-parser

Muy simple 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
fuente
1

Estaba usando, csv-parsepero para archivos más grandes tenía problemas de rendimiento, una de las mejores bibliotecas que he encontrado es Papa Parse , los documentos son buenos, buen soporte, liviano, sin dependencias.

Instalar en pc papaparse

npm install papaparse

Uso:

  • async / await
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()
  • llamar de vuelta
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 nota header: truees una opción en la configuración, consulte los documentos para ver otras opciones

Glen Thompson
fuente
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
fuente
0

Puede convertir csv a formato json usando el módulo csv-to-json y luego puede usar fácilmente el archivo json en su programa

Anuj Kumar
fuente
-1

npm instalar csv

Ejemplo de archivo CSV Necesitará un archivo CSV para analizar, por lo que ya tiene uno o puede copiar el texto a continuación y pegarlo en un archivo nuevo y llamar a ese archivo "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

Ejemplo de lectura y análisis de código del archivo CSV

Cree un nuevo archivo e inserte el siguiente código en él. Asegúrese de leer detenidamente lo que sucede detrás de escena.

    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 su aplicación y verifique la funcionalidad Abra una consola y escriba el siguiente comando:

Aplicación de nodo 1 Aplicación de nodo Debería ver el siguiente resultado en su consola:

[  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'},] Ahora debe abrir un navegador web y navegar hasta su servidor. Debería ver que genera los datos en formato JSON.

Conclusión Usando node.js y su módulo CSV podemos leer y usar rápida y fácilmente los datos almacenados en el servidor y ponerlos a disposición del cliente cuando lo solicite

Rubin bhandari
fuente