Запись большого CSV в JS файл с использованием Node FS - PullRequest
0 голосов
/ 13 апреля 2020

У меня большой файл CSV с данными почтового индекса (~ 1,1 ГБ), я пытаюсь отфильтровать нужные мне данные и затем записать массив значений в файл JS.

Проблема в том, что я всегда использую слишком много памяти и получаю эту ошибку:

Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Я попытался увеличить память с помощью этой команды: node --max-old-space-size = 4096 имя_файла. js но я все еще достигаю своего предела памяти, это занимает больше времени!

Вот мой код для записи в JS

const csvFilePath = "./data/postcodes.csv";
const csv = require("csvtojson");
const fs = require("fs");

csv()
    .fromFile(csvFilePath)
    .then((jsonArray) => {

        const inUsePostcodes = jsonArray.filter((x) => x["In Use?"] === "Yes").map((y) => y.Postcode);

        fs.writeFileSync("postcodes.js", inUsePostcodes);

    });

Вот Пример файла postcodes.csv:

Postcode,In Use?,Latitude,Longitude,Easting,Northing,Grid Ref,County,District,Ward,District Code,Ward Code,Country,County Code,Constituency,Introduced,Terminated,Parish,National Park,Population,Households,Built up area,Built up sub-division,Lower layer super output area,Rural/urban,Region,Altitude,London zone,LSOA Code,Local authority,MSOA Code,Middle layer super output area,Parish Code,Census output area,Constituency Code,Index of Multiple Deprivation,Quality,User Type,Last updated,Nearest station,Distance to station,Postcode area,Postcode district,Police force,Water company,Plus Code,Average Income
AB1 0AA,No,57.101474,-2.242851,385386,801193,NJ853011,"","Aberdeen City","Lower Deeside",S12000033,S13002843,Scotland,S99999999,"Aberdeen South",1980-01-01,1996-06-01,"","",,,"","","Cults, Bieldside and Milltimber West - 02","Accessible small town",,46,,S01006514,,S02001237,"Cults, Bieldside and Milltimber West",,S00090303,S14000002,6808,1,0,2020-02-19,"Portlethen",8.31408,AB,AB1,"Scotland","Scottish Water",9C9V4Q24+HV,
AB1 0AB,No,57.102554,-2.246308,385177,801314,NJ851013,"","Aberdeen City","Lower Deeside",S12000033,S13002843,Scotland,S99999999,"Aberdeen South",1980-01-01,1996-06-01,"","",,,"","","Cults, Bieldside and Milltimber West - 02","Accessible small town",,61,,S01006514,,S02001237,"Cults, Bieldside and Milltimber West",,S00090303,S14000002,6808,1,0,2020-02-19,"Portlethen",8.55457,AB,AB1,"Scotland","Scottish Water",9C9V4Q33+2F,
AB1 0AD,No,57.100556,-2.248342,385053,801092,NJ850010,"","Aberdeen City","Lower Deeside",S12000033,S13002843,Scotland,S99999999,"Aberdeen South",1980-01-01,1996-06-01,"","",,,"","","Cults, Bieldside and Milltimber West - 02","Accessible small town",,45,,S01006514,,S02001237,"Cults, Bieldside and Milltimber West",,S00090399,S14000002,6808,1,0,2020-02-19,"Portlethen",8.54352,AB,AB1,"Scotland","Scottish Water",9C9V4Q22+6M, 

Как мне записать в файл JS из этого CSV, не превышая моего предела памяти?

Ответы [ 2 ]

1 голос
/ 13 апреля 2020

Вам необходим анализатор потока csv, который будет анализировать его и выводить строку за раз, а затем передавать его в файл.

Вот один из способов сделать это с помощью модуля cvs-reader:

const fs = require('fs');
const csvReader = require('csv-reader');
const { Transform } = require('stream');

const myTransform = new Transform({
    readableObjectMode: true,
    writableObjectMode: true,
    transform(obj, encoding, callback) {
        let data = JSON.stringify(obj);
        if (this.tFirst) {
            // beginning of transformed data
            this.push("[");
            this.tFirst = false;
        } else {
            data = "," + data;    // add comma separator if not first object
        }
        this.push(data);
        callback();
  }
});
myTransform.tFirst = true;
myTransform._flush = function(callback) {
    // end of transformed data
    this.push("]");
    callback();
}

// All of these arguments are optional.
const options = { 
    skipEmptyLines: true,
    asObject: true,             // convert data to object
    parseNumbers: true, 
    parseBooleans: true, 
    trim: true 
};

const csvStream = new csvReader(options);
const readStream = fs.createReadStream('example.csv', 'utf8');
const writeStream = fs.createWriteStream('example.json', {autoClose: false});

readStream.on('error', err => {
     console.log(err);
     csvStream.destroy(err);
}).pipe(csvStream).pipe(myTransform).pipe(writeStream).on('error', err => {
    console.error(err);
}).on('finish', () => {
    console.log('done');
});
0 голосов
/ 13 апреля 2020

Проблема в том, что модуль узла csvto json пытается сохранить этот массивный jsonObj в памяти!

Я нашел другое решение, которое включает использование модуля узла csv-parser, а затем просто анализирует одну строку за раз вместо всего CSV!

Вот мое решение:

const csv = require('csv-parser');
const fs = require('fs');
var stream = fs.createWriteStream("postcodes.js", {flags:'a'});
let first = false;
fs.createReadStream('./data/postcodes.csv')
  .pipe(csv())
  .on('data', (row) => {
      if (row["In Use?"]) {
          if (!first) {
              first = true;
              stream.write(`const postcodes = ["${row.Postcode},\n"`);
          } else {
            stream.write(`"${row.Postcode},\n"`);
          }

      }
  })
  .on('end', () => {
      stream.write("]");
    console.log('CSV file successfully processed');
  });

Это не очень красивые строки, такие как const postcodes = для представления JavaScript, но оно выполняет нужную функцию.

...