Разбор файла CSV с помощью NodeJS


125

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

  1. читать csv построчно
  2. выполнять трудоемкую операцию на каждой линии
  3. перейти к следующей строке

Может ли кто-нибудь предложить здесь какие-либо альтернативные идеи?


Может быть, это вам поможет: stackoverflow.com/a/15554600/1169798
Sirko

1
Вы добавляли обратные вызовы для каждой строки? В противном случае он просто прочитает их все асинхронно.
Бен Форчун

Ответы:


81

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


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

4
fast-csv - это быстро, легко и быстро.
Roger

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модуль,
Ян

1
вы правы @lan, это должен быть csv-parseмодуль include .
Vineet

1
Отлично, спасибо, что подтвердили и обновили свой ответ!
Ян

3
Хорошее решение. Работает для меня.
Sun Bee,

3
к сожалению, это плохо - у меня есть ошибки с огромными файлами и длинными строками .... (ошибки памяти - хотя другие способы чтения - работают)
Сети

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
Я думаю, ты скучаешь по некоторым ')'?
Steven Luong 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-parser:

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

Чтобы приостановить потоковую передачу в fast-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 () и resume () - это то, что я искал! Моим приложениям всегда не хватало памяти, потому что они считывали данные намного быстрее, чем могли бы обработать.
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 тыс. Записей, и время обработки было на разумном уровне в десятки миллисекунд для всего набора.

Предложение решения jurka на основе потоков: node-csv IS-поток основан на потоковом API Node.js.


8

Модуль npm fast-csv может считывать данные построчно из файла 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()а на сайте проекта отсутствуют примеры и документация.
Сес Тиммерман,

3

Мне нужен был асинхронный csv-ридер, и изначально я попробовал ответить @Pransh Tiwari, но не смог заставить его работать с awaitи util.promisify(). В конце концов я наткнулся на node-csvtojson , который в значительной степени делает то же самое, что и csv-parser, но с обещаниями. Вот пример использования 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
Вопрос не был задан об установке плагина. Добавление некоторого кода, объясняющего, как использовать плагин, и / или объяснения, почему OP должен использовать его, было бы гораздо более выгодным.
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 в ваш записываемый поток, которым вы можете управлять вызовами asyncronus. Канал будет управлять буфером на всем пути обратно к считывателю, поэтому вы не столкнетесь с интенсивным использованием памяти

Простая версия

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


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.

Запустите приложение и проверьте работоспособность Откройте консоль и введите следующую команду:

Node app 1 Node app Вы должны увидеть в консоли следующий вывод:

[  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'},] Теперь вы должны открыть веб-браузер и перейти на свой сервер. Вы должны увидеть, что он выводит данные в формате JSON.

Заключение Используя node.js и его модуль CSV, мы можем быстро и легко прочитать и использовать данные, хранящиеся на сервере, и сделать их доступными для клиента по запросу.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.