Читать файл по одной строке в node.js? - PullRequest
500 голосов
/ 27 мая 2011

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

 var Lazy=require("lazy");
 new Lazy(process.stdin)
     .lines
     .forEach(
          function(line) { 
              console.log(line.toString()); 
          }
 );
 process.stdin.resume();

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

Я пытался:

 fs.open('./VeryBigFile.csv', 'r', '0666', Process);

 function Process(err, fd) {
    if (err) throw err;
    // DO lazy read 
 }

, но это не работает.Я знаю, что в крайнем случае я мог бы вернуться к использованию чего-то вроде PHP, но я хотел бы выяснить это.

Не думаю, что другой ответ будет работать, так как файл намного больше, чем у сервера, на котором он запущен, есть память.

Ответы [ 27 ]

7 голосов
/ 21 сентября 2015

Со времени публикации моего исходного ответа я обнаружил, что split - очень простой в использовании модуль узла для чтения строк в файле;Который также принимает необязательные параметры.

var split = require('split');
fs.createReadStream(file)
    .pipe(split())
    .on('data', function (line) {
      //each chunk now is a seperate line! 
    });

Не тестировался на очень больших файлах.Дайте нам знать, если вы делаете.

6 голосов
/ 21 мая 2014

Я был разочарован отсутствием комплексного решения для этого, поэтому я собрал свою собственную попытку ( git / npm ).Список возможностей, вставленный в копию:

  • Интерактивная обработка строк (на основе обратного вызова, без загрузки всего файла в ОЗУ)
  • При желании можно вернуть все строки в массиве (подробные или необработанные)режим)
  • Интерактивное прерывание потоковой передачи или выполнение обработки, подобной карте / фильтру
  • Обнаружение любого соглашения новой строки (ПК / Mac / Linux)
  • Правильная обработка eof / последней строки
  • Корректная обработка многобайтовых символов UTF-8
  • Получение информации о смещении байта и длине байта для каждой строки
  • Произвольный доступ с использованием смещения на основе строки или байта
  • Автоматически отображать информацию о смещении строки для ускорения произвольного доступа
  • Нулевые зависимости
  • Тесты

NIH?Вы решаете: -)

5 голосов
/ 08 февраля 2017

В большинстве случаев этого должно быть достаточно:

const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, file) => {
  const lines = file.split('\n')

  for (let line of lines)
    console.log(line)
});
5 голосов
/ 01 января 2012
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 голосов
/ 05 января 2013

Я хотел решить эту же проблему, в основном то, что было бы в Perl:

while (<>) {
    process_line($_);
}

Мой вариант использования был просто автономным сценарием, а не сервером, поэтому синхронность была в порядке. Это были мои критерии:

  • Минимальный синхронный код, который можно использовать во многих проектах.
  • Нет ограничений на размер файла или количество строк.
  • Нет ограничений на длину строк.
  • Может обрабатывать полный Юникод в UTF-8, включая символы за пределами BMP.
  • Способен обрабатывать * nix и окончания строк Windows (Mac старого образца мне не нужен).
  • Символы конца строки для включения в строки.
  • Может обрабатывать последнюю строку с символами конца строки или без них.
  • Не использовать внешние библиотеки, не включенные в дистрибутив node.js.

Это проект для меня, чтобы почувствовать низкоуровневый код типа сценариев в node.js и решить, насколько он жизнеспособен в качестве замены для других языков сценариев, таких как Perl.

После удивительных усилий и нескольких неудачных попыток вот код, который я придумал. Это довольно быстро, но менее тривиально, чем я ожидал: (раскошелиться на GitHub)

var fs            = require('fs'),
    StringDecoder = require('string_decoder').StringDecoder,
    util          = require('util');

function lineByLine(fd) {
  var blob = '';
  var blobStart = 0;
  var blobEnd = 0;

  var decoder = new StringDecoder('utf8');

  var CHUNK_SIZE = 16384;
  var chunk = new Buffer(CHUNK_SIZE);

  var eolPos = -1;
  var lastChunk = false;

  var moreLines = true;
  var readMore = true;

  // each line
  while (moreLines) {

    readMore = true;
    // append more chunks from the file onto the end of our blob of text until we have an EOL or EOF
    while (readMore) {

      // do we have a whole line? (with LF)
      eolPos = blob.indexOf('\n', blobStart);

      if (eolPos !== -1) {
        blobEnd = eolPos;
        readMore = false;

      // do we have the last line? (no LF)
      } else if (lastChunk) {
        blobEnd = blob.length;
        readMore = false;

      // otherwise read more
      } else {
        var bytesRead = fs.readSync(fd, chunk, 0, CHUNK_SIZE, null);

        lastChunk = bytesRead !== CHUNK_SIZE;

        blob += decoder.write(chunk.slice(0, bytesRead));
      }
    }

    if (blobStart < blob.length) {
      processLine(blob.substring(blobStart, blobEnd + 1));

      blobStart = blobEnd + 1;

      if (blobStart >= CHUNK_SIZE) {
        // blobStart is in characters, CHUNK_SIZE is in octets
        var freeable = blobStart / CHUNK_SIZE;

        // keep blob from growing indefinitely, not as deterministic as I'd like
        blob = blob.substring(CHUNK_SIZE);
        blobStart -= CHUNK_SIZE;
        blobEnd -= CHUNK_SIZE;
      }
    } else {
      moreLines = false;
    }
  }
}

Вероятно, это может быть очищено дальше, это было результатом проб и ошибок.

4 голосов
/ 09 мая 2019

Обновление в 2019

Потрясающий пример уже опубликован в официальной документации Nodejs. здесь

Для этого требуется, чтобы на вашем компьютере была установлена ​​последняя версия Nodejs. > 11,4

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

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();
2 голосов
/ 01 мая 2016
var fs = require('fs');

function readfile(name,online,onend,encoding) {
    var bufsize = 1024;
    var buffer = new Buffer(bufsize);
    var bufread = 0;
    var fd = fs.openSync(name,'r');
    var position = 0;
    var eof = false;
    var data = "";
    var lines = 0;

    encoding = encoding || "utf8";

    function readbuf() {
        bufread = fs.readSync(fd,buffer,0,bufsize,position);
        position += bufread;
        eof = bufread ? false : true;
        data += buffer.toString(encoding,0,bufread);
    }

    function getLine() {
        var nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl && eof) return fs.closeSync(fd), online(data,++lines), onend(lines); 
        if (!hasnl && !eof) readbuf(), nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl) return process.nextTick(getLine);
        var line = data.substr(0,nl);
        data = data.substr(nl+1);
        if (data[0] === "\n") data = data.substr(1);
        online(line,++lines);
        process.nextTick(getLine);
    }
    getLine();
}

У меня была такая же проблема, и я нашел решение, описанное выше. выглядит одинаково для других, но является aSync и может очень быстро читать большие файлы

Надеется, что это поможет

2 голосов
/ 21 июня 2015

Считыватель линий на основе генератора: https://github.com/neurosnap/gen-readlines

var fs = require('fs');
var readlines = require('gen-readlines');

fs.open('./file.txt', 'r', function(err, fd) {
  if (err) throw err;
  fs.fstat(fd, function(err, stats) {
    if (err) throw err;

    for (var line of readlines(fd, stats.size)) {
      console.log(line.toString());
    }

  });
});
2 голосов
/ 02 сентября 2015

Если вы хотите построчно прочитать файл и записать его в другом:

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

function readFileLineByLine(inputFile, outputFile) {

   var instream = fs.createReadStream(inputFile);
   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) {
        fs.appendFileSync(outputFile, line + '\n');
   });
};
1 голос
/ 17 января 2015

У меня есть небольшой модуль, который делает это хорошо и используется многими другими проектами npm readline Обратите внимание, что в узле v10 есть собственный модуль readline, поэтому я переиздал свой модуль как linebyline https://www.npmjs.com/package/linebyline

если вы не хотите использовать модуль, функция очень проста:

var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
util = require('util'),
newlines = [
  13, // \r
  10  // \n
];
var readLine = module.exports = function(file, opts) {
if (!(this instanceof readLine)) return new readLine(file);

EventEmitter.call(this);
opts = opts || {};
var self = this,
  line = [],
  lineCount = 0,
  emit = function(line, count) {
    self.emit('line', new Buffer(line).toString(), count);
  };
  this.input = fs.createReadStream(file);
  this.input.on('open', function(fd) {
    self.emit('open', fd);
  })
  .on('data', function(data) {
   for (var i = 0; i < data.length; i++) {
    if (0 <= newlines.indexOf(data[i])) { // Newline char was found.
      lineCount++;
      if (line.length) emit(line, lineCount);
      line = []; // Empty buffer.
     } else {
      line.push(data[i]); // Buffer new line data.
     }
   }
 }).on('error', function(err) {
   self.emit('error', err);
 }).on('end', function() {
  // Emit last line if anything left over since EOF won't trigger it.
  if (line.length){
     lineCount++;
     emit(line, lineCount);
  }
  self.emit('end');
 }).on('close', function() {
   self.emit('close');
 });
};
util.inherits(readLine, EventEmitter);
...