নোড.জেজে বিশাল লগফিলগুলি পার্স করা হচ্ছে - লাইন বাই লাইনে পড়ুন


125

জাভাস্ক্রিপ্ট / নোড.জেএস (আমি কিউব ব্যবহার করছি) এ বড় (5-10 গিগাবাইট) লগফিলের কিছু পার্সিং করা দরকার।

লগলাইনটি এমন দেখাচ্ছে:

10:00:43.343423 I'm a friendly log message. There are 5 cats, and 7 dogs. We are in state "SUCCESS".

আমাদের প্রতিটি লাইন পড়তে হবে, কিছু পার্সিং করতে হবে (উদাহরণস্বরূপ স্ট্রিপ আউট 5, 7এবং SUCCESS), তারপরে তাদের জেএস ক্লায়েন্ট ব্যবহার করে এই ডেটা কিউবে ( https://github.com/square/cube ) পাম্প করুন।

প্রথমত, নোডে রেখার দ্বারা একটি ফাইল, লাইনে প্রান্তিক উপায় কী?

এটি অনলাইনে মোটামুটি সাধারণ প্রশ্ন বলে মনে হচ্ছে:

অনেকগুলি উত্তর তৃতীয় পক্ষের মডিউলগুলির একগুচ্ছকে দেখায় বলে মনে হচ্ছে:

তবে এটি মোটামুটি বেসিক টাস্কের মতো মনে হচ্ছে - অবশ্যই, স্টাডলিবের মধ্যে একটি টেক্সটফাইলে পড়ার সহজ উপায় আছে, লাইন বাই লাইন?

দ্বিতীয়ত, আমার তখন প্রতিটি লাইন প্রক্রিয়া করা প্রয়োজন (যেমন টাইমস্ট্যাম্পকে একটি ডেট অবজেক্টে রূপান্তর করা এবং দরকারী ক্ষেত্রগুলি বের করা) ract

এটি করার সর্বোত্তম উপায় কী, মাধ্যমে আউটপুট সর্বাধিক? এমন কোনও উপায় আছে যা প্রতিটি লাইনে পড়ে বা কিউবে পাঠানোর পথে বাধা পাবে না?

তৃতীয়ত - আমি স্ট্রিং স্প্লিটগুলি ব্যবহার করে অনুমান করছি, এবং এর জেএস সমতুল্য (ইনডেক্স অফ! = -1?) রেগেক্সগুলির তুলনায় অনেক দ্রুত হবে? নোড.জেজে বড় অঙ্কের পাঠ্য ডেটা পার্স করার ক্ষেত্রে কারও কি অনেক অভিজ্ঞতা আছে?

চিয়ার্স, ভিক্টর


আমি নোডে একটি লগ পার্সার তৈরি করেছি যা জেএসএনে অন্তর্নির্মিত এবং আউটপুটগুলির সাথে 'ক্যাপচার' দিয়ে রেগেক্স স্ট্রিংগুলির একগুচ্ছ লাগে। এমনকি আপনি যদি কোনও ক্যালক করতে চান তবে প্রতিটি ক্যাপচারে ফাংশনগুলিও কল করতে পারেন। এটি আপনি যা করতে পারেন তা করতে পারে: npmjs.org/package/logax
জেস

উত্তর:


208

আমি একটি স্ট্রিম ব্যবহার করে খুব বড় ফাইল (জিবিএস) লাইনটি পার্স করার সমাধান অনুসন্ধান করেছি। সমস্ত তৃতীয় পক্ষের লাইব্রেরি এবং উদাহরণগুলি আমার প্রয়োজনীয়তার সাথে খাপ খায়নি যেহেতু তারা লাইন দ্বারা লাইন নয় (যেমন 1, 2, 3, 4 ..) ফাইলগুলি প্রক্রিয়া করে বা মেমরিটিতে পুরো ফাইলটি পড়ে

নীচের সমাধানটি স্ট্রম এবং পাইপ ব্যবহার করে এক এক করে লাইন খুব বড় ফাইল পার্স করতে পারে। পরীক্ষার জন্য আমি 17.000.000 রেকর্ড সহ একটি 2.1 জিবি ফাইল ব্যবহার করেছি। রামের ব্যবহার 60 এমবি ছাড়িয়ে যায়নি।

প্রথমে ইভেন্ট-স্ট্রিম প্যাকেজটি ইনস্টল করুন :

npm install event-stream

তারপর:

var fs = require('fs')
    , es = require('event-stream');

var lineNr = 0;

var s = fs.createReadStream('very-large-file.csv')
    .pipe(es.split())
    .pipe(es.mapSync(function(line){

        // pause the readstream
        s.pause();

        lineNr += 1;

        // process line here and call s.resume() when rdy
        // function below was for logging memory usage
        logMemoryUsage(lineNr);

        // resume the readstream, possibly from a callback
        s.resume();
    })
    .on('error', function(err){
        console.log('Error while reading file.', err);
    })
    .on('end', function(){
        console.log('Read entire file.')
    })
);

এখানে চিত্র বর্ণনা লিখুন

আমার এটা যায় কিভাবে জানাতে অনুগ্রহ করে!


6
এফওয়াইআই, এই কোডটি সিঙ্ক্রোনাস নয়। এটি অ্যাসিক্রোনাস। আপনি যদি console.log(lineNr)আপনার কোডের শেষ লাইনের পরে সন্নিবেশ করেন তবে এটি চূড়ান্ত লাইন গণনাটি দেখায় না কারণ ফাইলটি অবিচ্ছিন্নভাবে পড়া হয়েছে।
jender00

4
আপনাকে ধন্যবাদ, এটিই কেবলমাত্র একমাত্র সমাধান যা আমি খুঁজে পেতে পারি যা যখন থামার কথা ছিল এবং যখন এটি অনুমিত হয়েছিল তখন পুনরায় শুরু হয়েছিল। রিডলাইন হয়নি।
ব্রেন্ট 18

3
দুর্দান্ত উদাহরণ, এবং এটি আসলে বিরতি দেয়। অতিরিক্ত হিসাবে আপনি যদি ফাইলটি পড়া বন্ধ করার সিদ্ধান্ত নেন তাড়াতাড়ি আপনি ব্যবহার করতে পারেনs.end();
জিপজিট

2
কবজির মতো কাজ করেছেন। ইলাস্টিক অনুসন্ধান সূচকে এটি 150 মিলিয়ন ডকুমেন্টকে সূচকে ব্যবহার করে। readlineমডিউল একটি ব্যথা হয়। এটি বিরতি দেয় না এবং প্রতিবার 40-50 মিলিয়ন পরে ব্যর্থতা সৃষ্টি করে। একদিন নষ্ট। ধন্যবাদ উত্তরের জন্য অনেক। এই এক পুরোপুরি কাজ করে
মনদীপ সিং

3
ঘটনা-স্ট্রীম আপোস করা হয়েছিল: medium.com/intrinsic/... কিন্তু 4+ দৃশ্যত নিরাপদ blog.npmjs.org/post/180565383195/...
জন Vandivier

72

আপনি ইনবিল্ট readlineপ্যাকেজটি ব্যবহার করতে পারেন , এখানে ডক্স দেখুন । আমি একটি নতুন আউটপুট স্ট্রিম তৈরি করতে স্ট্রিম ব্যবহার করি ।

var fs = require('fs'),
    readline = require('readline'),
    stream = require('stream');

var instream = fs.createReadStream('/path/to/file');
var outstream = new stream;
outstream.readable = true;
outstream.writable = true;

var rl = readline.createInterface({
    input: instream,
    output: outstream,
    terminal: false
});

rl.on('line', function(line) {
    console.log(line);
    //Do your stuff ...
    //Then write to outstream
    rl.write(cubestuff);
});

বড় ফাইলগুলি প্রক্রিয়া করতে কিছুটা সময় নেবে। এটি কাজ করে যদি বলুন।


2
যেমন লেখা আছে, দ্বিতীয় থেকে শেষ লাইন ব্যর্থ হয়েছে কারণ কিউবেস্টফ সংজ্ঞায়িত হয়নি।
গ্রেগ

2
ব্যবহার করে readline, "ডু স্টাফ" অঞ্চলে অ্যাসিঙ্ক ক্রিয়াকলাপ সম্পাদনের জন্য পঠিত স্ট্রিমটিকে বিরতি / পুনঃসূচনা করা কি সম্ভব?
jchook

1
readlineআমি যখন বিরতি / পুনঃসূচনা করার চেষ্টা করছিলাম তখন @ জেচুক আমাকে প্রচুর সমস্যা দিচ্ছিল । এটি স্ট্রিম সঠিকভাবে সমস্যা অনেকটা তৈরি বিরাম নেই যদি স্রোতবরাবর প্রক্রিয়া মন্থর
Mandeep সিং

31

আমি সত্যিই @ জার্ডার উত্তরটি পছন্দ করেছি যা আসলে এখানে সঠিক উত্তর হওয়ার দাবিদার। আমি কিছু উন্নতি করেছি:

  • কোডটি একটি শ্রেণিতে (মডুলার)
  • পার্সিং অন্তর্ভুক্ত করা হয়েছে
  • পুনরায় শুরু করার ক্ষমতা বাইরের ক্ষেত্রে দেওয়া হয় যদি একটি অ্যাসিঙ্ক্রোনাস কাজ সিবিতে পড়তে যেমন ডিবিতে প্রবেশের জন্য আবদ্ধ থাকে বা এইচটিটিপি অনুরোধ থাকে
  • খণ্ড / ব্যাচে আকারে পড়া যা ব্যবহারকারী ঘোষণা করতে পারে। আপনার কাছে বিভিন্ন এনকোডিংয়ের ফাইল রয়েছে সে ক্ষেত্রে আমি প্রবাহেও এনকোডিংয়ের যত্ন নিয়েছিলাম।

কোডটি এখানে:

'use strict'

const fs = require('fs'),
    util = require('util'),
    stream = require('stream'),
    es = require('event-stream'),
    parse = require("csv-parse"),
    iconv = require('iconv-lite');

class CSVReader {
  constructor(filename, batchSize, columns) {
    this.reader = fs.createReadStream(filename).pipe(iconv.decodeStream('utf8'))
    this.batchSize = batchSize || 1000
    this.lineNumber = 0
    this.data = []
    this.parseOptions = {delimiter: '\t', columns: true, escape: '/', relax: true}
  }

  read(callback) {
    this.reader
      .pipe(es.split())
      .pipe(es.mapSync(line => {
        ++this.lineNumber

        parse(line, this.parseOptions, (err, d) => {
          this.data.push(d[0])
        })

        if (this.lineNumber % this.batchSize === 0) {
          callback(this.data)
        }
      })
      .on('error', function(){
          console.log('Error while reading file.')
      })
      .on('end', function(){
          console.log('Read entirefile.')
      }))
  }

  continue () {
    this.data = []
    this.reader.resume()
  }
}

module.exports = CSVReader

সুতরাং মূলত, আপনি এখানে এটি কীভাবে ব্যবহার করবেন:

let reader = CSVReader('path_to_file.csv')
reader.read(() => reader.continue())

আমি এটি একটি 35 জিবি সিএসভি ফাইল দিয়ে পরীক্ষা করেছি এবং এটি আমার পক্ষে কাজ করেছে এবং সে কারণেই আমি @ জার্ডারের উত্তরে এটি তৈরি করতে বেছে নিয়েছি , ফিডব্যাকগুলি স্বাগত।


কত সময় লাগল?
জেড খোলা

স্পষ্টতই, এই pause()কল অভাব আছে , তাই না?
ভানুয়ান

এছাড়াও, এটি কলব্যাক ফাংশনটি কল করে না। সুতরাং যদি ব্যাচসাইজ 100 হয়, ফাইলের আকার 150 হয় তবে কেবল 100 টি আইটেম প্রক্রিয়া করা হবে। আমি কি ভূল?
ভানুয়ান

16

আমি একটি টেক্সট ফাইল থেকে 1 000 000 লাইনের বেশি পড়ার জন্য https://www.npmjs.com/package/line-by-line ব্যবহার করেছি । এই ক্ষেত্রে, র‌্যামের একটি দখল ক্ষমতা প্রায় 50-60 মেগাবাইট ছিল।

    const LineByLineReader = require('line-by-line'),
    lr = new LineByLineReader('big_file.txt');

    lr.on('error', function (err) {
         // 'err' contains error object
    });

    lr.on('line', function (line) {
        // pause emitting of lines...
        lr.pause();

        // ...do your asynchronous line processing..
        setTimeout(function () {
            // ...and continue emitting lines.
            lr.resume();
        }, 100);
    });

    lr.on('end', function () {
         // All lines are read, file is closed now.
    });

'লাইন বাই লাইন' নির্বাচিত উত্তরের চেয়ে মেমরির দক্ষ। সিএসভিতে 1 মিলিয়ন লাইনের জন্য নির্বাচিত উত্তরের আমার নোড প্রক্রিয়াটি কম 800 এর মেগাবাইটে ছিল। 'লাইন বাই লাইন' ব্যবহার করে এটি ধারাবাহিকভাবে কম 700 এর দশকে ছিল। এই মডিউলটি কোডটি পরিষ্কার এবং সহজেই পড়তে পারে। মোট আমাকে প্রায় 18 মিলিয়ন পড়তে হবে যাতে প্রতি এমবি গণনা করা হয়!
নিও ২

এটি লজ্জার বিষয় এটি স্ট্যান্ডার্ড 'অংশ' এর পরিবর্তে এটি নিজস্ব ইভেন্ট 'লাইন' ব্যবহার করে, যার অর্থ আপনি 'পাইপ' ব্যবহার করতে পারবেন না।
রিনি উলার

কয়েক ঘন্টা পরীক্ষার পরে এবং অনুসন্ধানের পরে এটিই একমাত্র সমাধান যা প্রকৃতপক্ষে lr.cancel()পদ্ধতিতে থামে । 1 এমএসে 5 জিগ ফাইলের প্রথম 1000 লাইন পড়ে। অসাধারণ!!!!
পেরেজ লামেড ভ্যান নিকের্ক

6

বড় ফাইল লাইন এক এক করে পড়া ছাড়াও আপনি এটিকে খণ্ড খণ্ড করে পড়তে পারেন। আরও নিবন্ধটি দেখুন

var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
    offset += bytesRead;
    var str = chunkBuffer.slice(0, bytesRead).toString();
    var arr = str.split('\n');

    if(bytesRead = chunkSize) {
        // the last item of the arr may be not a full line, leave it to the next chunk
        offset -= arr.pop().length;
    }
    lines.push(arr);
}
console.log(lines);

এটা কি হতে পারে যে, নিম্নলিখিত একটি কাজ পরিবর্তে একটি তুলনা হওয়া উচিত: if(bytesRead = chunkSize)?
স্টেফান রেইন

4

নোড.জেএস ডকুমেন্টেশন রিডলাইন মডিউলটি ব্যবহার করে খুব মার্জিত উদাহরণ দেয়।

উদাহরণ: ফাইল স্ট্রিম লাইন বাই লাইন পড়ুন

const fs = require('fs');
const readline = require('readline');

const rl = readline.createInterface({
    input: fs.createReadStream('sample.txt'),
    crlfDelay: Infinity
});

rl.on('line', (line) => {
    console.log(`Line from file: ${line}`);
});

দ্রষ্টব্য: আমরা CRFDelay বিকল্পটি সিআর এলএফ ('all r \ n') এর একক লাইনের বিরতি হিসাবে স্বীকৃতি জানাতে ব্যবহার করি।


3

আমি এখনও একই সমস্যা ছিল। এই বৈশিষ্ট্যটি বলে মনে হচ্ছে এমন বেশ কয়েকটি মডিউল তুলনা করার পরে, আমি নিজেই এটি করার সিদ্ধান্ত নিয়েছি, এটি আমার যা ভাবার চেয়ে সহজ।

গিস্ট: https://gist.github.com/deemstone/8279565

var fetchBlock = lineByline(filepath, onEnd);
fetchBlock(function(lines, start){ ... });  //lines{array} start{int} lines[0] No.

এটি একটি ক্লোজারে খোলা ফাইলটি কভার করে, এটি fetchBlock()ফিরলে ফাইলটি একটি ব্লক আনবে, অ্যারেতে বিভক্ত হয়ে যাবে (শেষ আনয়ন থেকে বিভাগটি করবে)।

প্রতিটি পড়ার ক্রিয়াকলাপের জন্য আমি ব্লকের আকার 1024 এ সেট করেছি। এটিতে বাগ থাকতে পারে তবে কোড যুক্তিটি সুস্পষ্ট, নিজে চেষ্টা করে দেখুন।


2

নোড-বাইলাইন স্ট্রিম ব্যবহার করে, তাই আমি আপনার বিশাল ফাইলগুলির জন্য সেটিকে পছন্দ করব।

আপনার তারিখ-রূপান্তরগুলির জন্য আমি moment.js ব্যবহার করব

আপনার মাধ্যমে আউটপুট সর্বাধিকীকরণের জন্য আপনি একটি সফ্টওয়্যার-ক্লাস্টার ব্যবহারের বিষয়ে ভাবতে পারেন। কিছু সুন্দর-মডিউল রয়েছে যা নোড-নেটিভ ক্লাস্টার-মডিউলটি বেশ ভালভাবে গুটিয়ে রাখে। আমি ইস্যাকস থেকে ক্লাস্টার-মাস্টার পছন্দ করি। যেমন আপনি এক্স কর্মীদের একটি ক্লাস্টার তৈরি করতে পারেন যা সমস্ত একটি ফাইল গণনা করে।

বেঞ্চমার্কিং স্প্লিটের জন্য বনাম রেজিেক্সগুলি বেঞ্চমার্ক.জেএস ব্যবহার করে । আমি এখন পর্যন্ত এটি পরীক্ষা করেছি। বেঞ্চমার্ক.জেএস নোড-মডিউল হিসাবে উপলব্ধ


2

এই প্রশ্নের উত্তরের উপর ভিত্তি করে আমি এমন একটি ক্লাস প্রয়োগ করেছি যাতে আপনি ফাইলগুলি সমকালীনভাবে লাইন বাই লাইনে পড়তে পারেন fs.readSync()। আপনি এই Qপ্রতিশ্রুতি ব্যবহার করে এই "বিরতি" এবং "পুনঃসূচনা" করতে পারেন ( jQueryমনে হয় এটি একটি ডোম প্রয়োজন যাতে এটি চালানো যায় না nodejs):

var fs = require('fs');
var Q = require('q');

var lr = new LineReader(filenameToLoad);
lr.open();

var promise;
workOnLine = function () {
    var line = lr.readNextLine();
    promise = complexLineTransformation(line).then(
        function() {console.log('ok');workOnLine();},
        function() {console.log('error');}
    );
}
workOnLine();

complexLineTransformation = function (line) {
    var deferred = Q.defer();
    // ... async call goes here, in callback: deferred.resolve('done ok'); or deferred.reject(new Error(error));
    return deferred.promise;
}

function LineReader (filename) {      
  this.moreLinesAvailable = true;
  this.fd = undefined;
  this.bufferSize = 1024*1024;
  this.buffer = new Buffer(this.bufferSize);
  this.leftOver = '';

  this.read = undefined;
  this.idxStart = undefined;
  this.idx = undefined;

  this.lineNumber = 0;

  this._bundleOfLines = [];

  this.open = function() {
    this.fd = fs.openSync(filename, 'r');
  };

  this.readNextLine = function () {
    if (this._bundleOfLines.length === 0) {
      this._readNextBundleOfLines();
    }
    this.lineNumber++;
    var lineToReturn = this._bundleOfLines[0];
    this._bundleOfLines.splice(0, 1); // remove first element (pos, howmany)
    return lineToReturn;
  };

  this.getLineNumber = function() {
    return this.lineNumber;
  };

  this._readNextBundleOfLines = function() {
    var line = "";
    while ((this.read = fs.readSync(this.fd, this.buffer, 0, this.bufferSize, null)) !== 0) { // read next bytes until end of file
      this.leftOver += this.buffer.toString('utf8', 0, this.read); // append to leftOver
      this.idxStart = 0
      while ((this.idx = this.leftOver.indexOf("\n", this.idxStart)) !== -1) { // as long as there is a newline-char in leftOver
        line = this.leftOver.substring(this.idxStart, this.idx);
        this._bundleOfLines.push(line);        
        this.idxStart = this.idx + 1;
      }
      this.leftOver = this.leftOver.substring(this.idxStart);
      if (line !== "") {
        break;
      }
    }
  }; 
}

0
import * as csv from 'fast-csv';
import * as fs from 'fs';
interface Row {
  [s: string]: string;
}
type RowCallBack = (data: Row, index: number) => object;
export class CSVReader {
  protected file: string;
  protected csvOptions = {
    delimiter: ',',
    headers: true,
    ignoreEmpty: true,
    trim: true
  };
  constructor(file: string, csvOptions = {}) {
    if (!fs.existsSync(file)) {
      throw new Error(`File ${file} not found.`);
    }
    this.file = file;
    this.csvOptions = Object.assign({}, this.csvOptions, csvOptions);
  }
  public read(callback: RowCallBack): Promise < Array < object >> {
    return new Promise < Array < object >> (resolve => {
      const readStream = fs.createReadStream(this.file);
      const results: Array < any > = [];
      let index = 0;
      const csvStream = csv.parse(this.csvOptions).on('data', async (data: Row) => {
        index++;
        results.push(await callback(data, index));
      }).on('error', (err: Error) => {
        console.error(err.message);
        throw err;
      }).on('end', () => {
        resolve(results);
      });
      readStream.pipe(csvStream);
    });
  }
}
import { CSVReader } from '../src/helpers/CSVReader';
(async () => {
  const reader = new CSVReader('./database/migrations/csv/users.csv');
  const users = await reader.read(async data => {
    return {
      username: data.username,
      name: data.name,
      email: data.email,
      cellPhone: data.cell_phone,
      homePhone: data.home_phone,
      roleId: data.role_id,
      description: data.description,
      state: data.state,
    };
  });
  console.log(users);
})();

-1

অ্যাসিঙ্ক্রোনালি পাঠ্য বা জেএসওএন পড়ার জন্য আমি একটি নোড মডিউল তৈরি করেছি। বড় ফাইলগুলিতে পরীক্ষিত।

var fs = require('fs')
, util = require('util')
, stream = require('stream')
, es = require('event-stream');

module.exports = FileReader;

function FileReader(){

}

FileReader.prototype.read = function(pathToFile, callback){
    var returnTxt = '';
    var s = fs.createReadStream(pathToFile)
    .pipe(es.split())
    .pipe(es.mapSync(function(line){

        // pause the readstream
        s.pause();

        //console.log('reading line: '+line);
        returnTxt += line;        

        // resume the readstream, possibly from a callback
        s.resume();
    })
    .on('error', function(){
        console.log('Error while reading file.');
    })
    .on('end', function(){
        console.log('Read entire file.');
        callback(returnTxt);
    })
);
};

FileReader.prototype.readJSON = function(pathToFile, callback){
    try{
        this.read(pathToFile, function(txt){callback(JSON.parse(txt));});
    }
    catch(err){
        throw new Error('json file is not valid! '+err.stack);
    }
};

ফাইলটি কেবল ফাইল-রিডার.জেজেস হিসাবে সংরক্ষণ করুন এবং এটি ব্যবহার করুন:

var FileReader = require('./file-reader');
var fileReader = new FileReader();
fileReader.readJSON(__dirname + '/largeFile.json', function(jsonObj){/*callback logic here*/});

7
দেখে মনে হচ্ছে আপনি জেরার্ডের উত্তর থেকে অনুলিপি করেছেন। আপনি যে অংশটি অনুলিপি করেছেন তার জন্য আপনার জেরার্ড ক্রেডিট দেওয়া উচিত।
পল লিঞ্চ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.