Розбір файлу CSV за допомогою NodeJS


125

За допомогою nodejs я хочу проаналізувати .csv файл із 10000 записів і виконати деяку операцію в кожному рядку. Я спробував використовувати http://www.adaltas.com/projects/node-csv . Я не міг змусити це робити паузу в кожному рядку. Це просто читає всі 10000 записів. Мені потрібно зробити наступне:

  1. читати csv рядок за рядком
  2. виконувати трудомісткі операції на кожному рядку
  3. перейти до наступного рядка

Чи можете будь-хто запропонувати будь-які альтернативні ідеї тут?


Можливо, це допоможе вам: stackoverflow.com/a/15554600/1169798
Сірко

1
Ви додавали зворотні дзвінки для кожного рядка? Інакше просто перечитати їх усі асинхронно.
Бен Фортун

Відповіді:


81

Схоже, вам потрібно використовувати якесь потокове рішення, існували вже такі бібліотеки, тому перш ніж винаходити себе, спробуйте цю бібліотеку, яка також включає підтримку перевірки. https://www.npmjs.org/package/fast-csv


27
NodeCSV також добре підтримується і, мабуть, має приблизно на порядок більше користувачів. npmjs.com/package/csv
підключений парою

4
fast-csv - це швидкий, простий у користуванні та початок роботи.
Roger Garzon Nieto

1
Він підтримує URL-адресу?
DMS-KH

57

Я використовував такий спосіб: -

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
Я можу робити щось не так, але коли я запускаю це, parseне визначається. Щось мені не вистачає? Коли я запускаю, npm install csv-parseа потім додаю в свій код var parse = require("csv-parse");, тоді він працює. Ви впевнені, що ваші твори працюють? Так чи інакше, я люблю це рішення (навіть якщо мені доведеться включити csv-parseмодуль
Ian

1
ви правий @lan, це повинен включати csv-parseмодуль.
виноград

1
Чудово, дякую за перевірку та оновлення своєї відповіді!
Ян

3
Приємне рішення. Працює для мене.
Sun Bee

3
на жаль це погано - у мене виникли помилки з величезними файлами та довгими рядками .... (помилки в пам’яті - хоча інші способи її читання - працює)
Seti

55

Моє поточне рішення використовує модуль async для виконання послідовно:

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
Я думаю, ти сумуєш за деякими ')'?
Стівен Луонг C

Я думаю, що додавання ")" до кінця рядків 14 та 15 має вирішити проблему.
Джон

@ShashankVivek - у цій старій відповіді (з 2015 року) "async" - це бібліотека npm, яка використовується. Більше про це тут caolan.github.io/async - щоб зрозуміти, чому, можливо, це допомагає blog.risingstack.com/node-hero-async-programming-in-node-js Але javascript дуже розвинувся з 2015 року, і якщо ваше питання докладніше про асинхронізацію загалом, тоді прочитайте цю останню статтю medium.com/@tkssharma/…
prule

15
  • Це рішення використовується csv-parserзамість csv-parseвикористовуваного в деяких відповідях вище.
  • csv-parserз'явився приблизно через 2 роки csv-parse.
  • Вони обидва вирішують одну і ту ж мету, але особисто я знайшов csv-parserкраще, так як через неї легко обробляти заголовки.

Встановіть csv-аналізатор спочатку:

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

Для подальшого читання див


13

Щоб призупинити потокове передавання у швидкому 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 () та резюме () - це те, що я шукав! У моїх програмах завжди не вистачало б пам’яті, оскільки вона читала дані набагато швидше, ніж те, що вона могла обробити.
ehrhardt

@adnan Дякуємо, що вказали на це. Це не згадується в документації, і саме це я шукав.
Піюш Белі

10

Проект 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());
    });
});

Зі свого досвіду, я можу сказати, що це також досить швидка реалізація, я працював з ним над наборами даних з близько 10 к записів, і час обробки був на розумному рівні в десятки мілісекунд для всього набору.

Rearding Jurka «потік пропозицію рішень , заснованих з: вузол-CSV IS потоку на основі і слід Node.js» потокового API.


8

Модуль швидкого 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");
 });

1
fast-csv@4.0.2 не має, fromStream()і на його проектному сайті не вистачає прикладів та документації.
Cees Timmerman

3

Мені потрібен був читач Csv асинхронного і спочатку намагався відповісти @Pransh Тиварі, але не міг змусити його працювати з awaitі util.promisify(). Врешті-решт я натрапив на node-csvtojson , який в значній мірі робить те саме, що і csv-аналізатор, але з обіцянками. Ось приклад використання 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);
    });
};

2

Спробуйте плагін по рядку npm-плагін.

npm install line-by-line --save

5
Встановлення плагіна не було запитанням. Додавання коду, щоб пояснити, як користуватися плагіном та / або пояснити, чому ОП слід використовувати, було б набагато вигідніше.
domdambrogia

2

це моє рішення отримати CSV-файл із зовнішньої URL-адреси

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

Обхід для виконання цього завдання з функцією await / async :

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

2

Гаразд, тут є багато відповідей, і я не думаю, що вони відповідають на ваше запитання, яке, на мою думку, схоже на моє.

Вам потрібно зробити таку операцію, як звернення до бази даних або третьої частини api, яка потребує часу та є асинхронією. Ви не хочете завантажувати весь документ у пам'ять через велику чи іншу причину, тому вам потрібно читати рядок за рядком для обробки.

Я прочитав документи fs, і це може призупинитись при читанні, але використання дзвінка .on ('data') зробить нескінченним, яке більшість із цих відповідей використовує та спричинить проблему.


ОНОВЛЕННЯ: Я знаю більше інформації про потоки, ніж я коли-небудь хотів

Найкращий спосіб зробити це - створити потік, що записується. Це передасть дані 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 ('читабельний') вдруге викликається на малих та великих файлах. Я не знаю чому, але це блокує те, що під час другого процесу читають ті самі позиції.


1

Я використовую цей простий: 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'])
  });

1

Я використовував, 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- це параметр у конфігурації, див. Документи для інших параметрів


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

Ви можете конвертувати CSV у формат json, використовуючи модуль csv-to-json, і тоді ви можете легко використовувати файл json у своїй програмі


-1

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: 'POGS'}, 4 MYCSV {Fieldone: '441', Fieldtwo: 'CHEESE, Fieldthree:' CARMEL '}, 5 MYCSV {Fieldone:' 221 ', Fieldtwo: 'ABC', Fieldthree: 'ДОМА'},] Тепер ви повинні відкрити веб-браузер і перейти до свого сервера. Ви повинні побачити, як вони виводять дані у форматі JSON.

Висновок Використовуючи node.js та його модуль CSV, ми можемо швидко та легко читати та використовувати дані, що зберігаються на сервері, та надавати їх клієнтам на запит

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.