Nodejs - чтение строки за строкой из файла, выполнение асинхронного действия для каждой строки и повторное использование

Я пытаюсь прочитать файл по строкам, выполнить некоторые действия, которые имеют обратный вызов, и когда функция закончит возобновление чтения строки. Например:

var fs = require('fs');
var readline = require('readline');
var stream = require('stream');
var instream = fs.createReadStream('./phrases.txt');
var outstream = new stream;
var rl = readline.createInterface(instream, outstream);
rl.on('line', function (line) {
  rl.pause();
  setTimeout(function () {
    console.log('resuming');
    rl.resume();
  }, 2000);
});

Я был под впечатлением, что приведенный выше пример должен в основном читать строку, ждать 2 секунды, console.log а затем перейти к следующей строке. Что действительно происходит, так это то, что он ждет первые 2 секунды, а затем извергает много console.log

Ответ 1

Модуль Line by Line помогает вам читать большие текстовые файлы по строкам, не забирая файлы в память.

Вы можете обрабатывать линии асинхронно. Это пример:

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

Ответ 2

Очень хороший модуль чтения строк существует, https://github.com/nickewing/line-reader

простой код:

 var lineReader = require('line-reader');
   lineReader.eachLine('file.txt', function(line, last) {
      // do whatever you want with line...
      console.log(line);
      if(last){
         // or check if it the last one
      }
   });

также интерфейс "java-style" для большего контроля:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});

Еще одно замечательное решение:

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

var rd = readline.createInterface({
    input: fs.createReadStream('phrases.txt'),
    output: process.stdout,
    terminal: false
});

rd.on('line', function(line) {
    console.log('-------')
    console.log(line);
    sleep.sleep(2)

});

Ответ 3

function createLineReader(fileName){
    var EM = require("events").EventEmitter
    var ev = new EM()
    var stream = require("fs").createReadStream(fileName)
    var remainder = null;
    stream.on("data",function(data){
        if(remainder != null){//append newly received data chunk
            var tmp = new Buffer(remainder.length+data.length)
            remainder.copy(tmp)
            data.copy(tmp,remainder.length)
            data = tmp;
        }
        var start = 0;
        for(var i=0; i<data.length; i++){
            if(data[i] == 10){ //\n new line
                var line = data.slice(start,i)
                ev.emit("line", line)
                start = i+1;
            }
        }
        if(start<data.length){
            remainder = data.slice(start);
        }else{
            remainder = null;
        }
    })

    stream.on("end",function(){
        if(null!=remainder) ev.emit("line",remainder)
    })

    return ev
}


//---------main---------------
fileName = process.argv[2]

lineReader = createLineReader(fileName)
lineReader.on("line",function(line){
    console.log(line.toString())
    //console.log("++++++++++++++++++++")
})

Ответ 4

Вот простое решение для машинописного текста, использующее средство чтения строк, которое может работать в nodejs 8:

import lineReader from 'line-reader';

function readLines(filename: string, processLine: (line: string) => Promise<void>): Promise<void> {
  return new Promise((resolve, reject) => {
    lineReader.eachLine(filename, (line, last, callback) => {
      if (!callback) throw new Error('panic');
      processLine(line)
        .then(() => last ? resolve() : callback())
        .catch(reject);
    });
  });
}

async function echo(): Promise<void> {
  await readLines('/dev/stdin', async (line) => {
    console.log(line);
  });
}

echo();

Обратите внимание, что он не буферизирует весь файл перед выполнением, поэтому он подходит для обработки больших текстовых файлов.