Прочитайте файл по одной строке за раз в node.js?

Я пытаюсь прочитать большой файл по одной строке за раз. Я нашел вопрос о 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, но я хотел бы понять это.

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

Ответ 1

Начиная с Node.js v0.12 и начиная с Node.js v4.0.0, существует стабильный основной модуль readline. Вот самый простой способ чтения строк из файла без каких-либо внешних модулей:

var lineReader = require('readline').createInterface({
  input: require('fs').createReadStream('file.in')
});

lineReader.on('line', function (line) {
  console.log('Line from file:', line);
});

Последняя строка читается правильно (начиная с Node v0.12 или более поздней версии), даже если не существует final \n.

ОБНОВЛЕНИЕ: этот пример был добавлен в официальную документацию Node API.

Ответ 2

Для такой простой операции не должно быть никакой зависимости от сторонних модулей. Легко.

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

var rd = readline.createInterface({
    input: fs.createReadStream('/path/to/file'),
    output: process.stdout,
    console: false
});

rd.on('line', function(line) {
    console.log(line);
});

Ответ 3

Вам не нужно open файла, но вместо этого вам нужно создать ReadStream.

fs.createReadStream

Затем передайте этот поток в Lazy

Ответ 4

есть очень хороший модуль для чтения файла построчно, он называется line-reader

с этим вы просто пишете:

var lineReader = require('line-reader');

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

Вы можете даже перебрать файл с помощью интерфейса в стиле java, если вам нужно больше контроля:

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

Ответ 5

require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})

Ответ 6

Старая тема, но это работает:

var rl = readline.createInterface({
      input : fs.createReadStream('/path/file.txt'),
      output: process.stdout,
      terminal: false
})
rl.on('line',function(line){
     console.log(line) //or parse line
})

Simple. Нет необходимости в внешнем модуле.

Ответ 7

Вы всегда можете свернуть свой собственный линейный ридер. Я еще не тестировал этот фрагмент, но он правильно разбивает входящий поток кусков на строки без конечных "\n"

var last = "";

process.stdin.on('data', function(chunk) {
    var lines, i;

    lines = (last+chunk).split("\n");
    for(i = 0; i < lines.length - 1; i++) {
        console.log("line: " + lines[i]);
    }
    last = lines[i];
});

process.stdin.on('end', function() {
    console.log("line: " + last);
});

process.stdin.resume();

Я придумал это, работая над быстрым анализом журнала script, который должен был накапливать данные во время разбора журнала, и я чувствовал, что было бы неплохо попробовать это сделать с помощью js и node вместо использования perl или bash.

В любом случае, я чувствую, что маленькие скрипты nodejs должны быть автономными и не полагаться на сторонние модули, поэтому после прочтения всех ответов на этот вопрос, каждый из которых использует различные модули для обработки разбора строк, может быть найдено 13 SLOC native nodejs solution.

Ответ 8

С несущим модулем:

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});

Ответ 9

В итоге я столкнулся с массивной массивной утечкой памяти, когда Lazy читал строки за строкой, пытаясь обработать эти строки и записать их в другой поток из-за того, как работает дренаж/пауза/возобновление в node (см. http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (я люблю этого парня кстати)). Я не очень внимательно смотрел на Lazy, чтобы понять, почему именно, но я не мог приостановить чтение потока, чтобы дать возможность без утечки.

Я написал код для обработки массивных CSV файлов в xml-документах, здесь вы можете увидеть код: https://github.com/j03m/node-csv2xml

Если вы запустили предыдущие версии с Lazy line, она протекает. Последняя версия не протекает вообще, и вы, вероятно, можете использовать ее в качестве основы для чтения/процессора. Хотя у меня есть некоторые вещи.

Изменить: Я думаю, я должен также отметить, что мой код с Lazy работал нормально, пока я не обнаружил, что пишу достаточно большие фрагменты xml, которые сбрасывают/приостанавливают/возобновляют, потому что это необходимо. Для небольших кусков было хорошо.

Ответ 10

Edit:

Используйте трансформировать поток.


С помощью BufferedReader вы можете читать строки.

new BufferedReader ("lorem ipsum", { encoding: "utf8" })
    .on ("error", function (error){
        console.log ("error: " + error);
    })
    .on ("line", function (line){
        console.log ("line: " + line);
    })
    .on ("end", function (){
        console.log ("EOF");
    })
    .read ();

Ответ 11

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

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

Не тестировались на очень большие файлы. Сообщите нам, если вы это сделаете.

Ответ 12

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

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

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

Ответ 13

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("++++++++++++++++++++")
})

Ответ 14

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

const fs = require("fs")

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

  for (let line of lines)
    console.log(line)
});

Ответ 15

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

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

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

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

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

После неожиданного усилия и нескольких ложных запусков это код, который я придумал. Это довольно быстро, но менее тривиально, чем я ожидал: (fork it on 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;
    }
  }
}

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

Ответ 16

Обновление в 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();

Ответ 17

Устройство чтения на основе генератора: 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());
    }

  });
});

Ответ 18

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

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');
   });
};

Ответ 19

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();
}

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

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

Ответ 20

У меня есть небольшой модуль, который делает это хорошо и используется довольно многими другими проектами. npm readline Обратите внимание на node 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);

Ответ 21

Другое решение - запустить логику через последовательный исполнитель nsynjs. Он читает файл по строкам с помощью модуля node readline, и он не использует promises или рекурсию, поэтому не будет работать с большими файлами. Вот как выглядит код:

var nsynjs = require('nsynjs');
var textFile = require('./wrappers/nodeReadline').textFile; // this file is part of nsynjs

function process(textFile) {

    var fh = new textFile();
    fh.open('path/to/file');
    var s;
    while (typeof(s = fh.readLine(nsynjsCtx).data) != 'undefined')
        console.log(s);
    fh.close();
}

var ctx = nsynjs.run(process,{},textFile,function () {
    console.log('done');
});

Код выше основан на этом примере: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js

Ответ 22

i использую это:

function emitLines(stream, re){
    re = re && /\n/;
    var buffer = '';

    stream.on('data', stream_data);
    stream.on('end', stream_end);

    function stream_data(data){
        buffer += data;
        flush();
    }//stream_data

    function stream_end(){
        if(buffer) stream.emmit('line', buffer);
    }//stream_end


    function flush(){
        var re = /\n/;
        var match;
        while(match = re.exec(buffer)){
            var index = match.index + match[0].length;
            stream.emit('line', buffer.substring(0, index));
            buffer = buffer.substring(index);
            re.lastIndex = 0;
        }
    }//flush

}//emitLines

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

г -

Ответ 23

В то время как вы, вероятно, должны использовать модуль readline, как показывает верхний ответ, readline кажется ориентированным на интерфейсы командной строки, а не на чтение строки. Это также немного более непрозрачно относительно буферизации. (Любой, кому нужен ориентированный на потоковое чтение читатель, вероятно, захочет изменить размеры буфера). Модуль readline составляет ~ 1000 строк, тогда как при статистике и тестах 34.

const EventEmitter = require('events').EventEmitter;
class LineReader extends EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.totalChars = 0;
        this.totalLines = 0;
        this.leftover = '';

        f.on('data', (chunk)=>{
            this.totalChars += chunk.length;
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) lines.pop();
            this.totalLines += lines.length;
            for (let l of lines) this.onLine(l);
        });
        // f.on('error', ()=>{});
        f.on('end', ()=>{console.log('chars', this.totalChars, 'lines', this.totalLines)});
    }
    onLine(l){
        this.emit('line', l);
    }
}
//Command line test
const f = require('fs').createReadStream(process.argv[2], 'utf8');
const delim = process.argv[3];
const lineReader = new LineReader(f, delim);
lineReader.on('line', (line)=> console.log(line));

Здесь еще более короткая версия, без статистики, в 19 строках:

class LineReader extends require('events').EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.leftover = '';
        f.on('data', (chunk)=>{
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) 
                lines.pop();
            for (let l of lines)
                this.emit('line', l);
        });
    }
}

Ответ 24

const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, data) => {
var innerContent;
    console.log("Asynchronous read: " + data.toString());
    const lines = data.toString().split('\n')
    for (let line of lines)
        innerContent += line + '<br>';


});

Ответ 25

Я оборачиваю всю логику ежедневной обработки строк в виде модуля npm: line-kit https://www.npmjs.com/package/line-kit

// example
var count = 0
require('line-kit')(require('fs').createReadStream('/etc/issue'),
                    (line) => { count++; },
                    () => {console.log('seen ${count} lines')})

Ответ 26

Я сделал Nexline

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

Ответ 27

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

(function () {
  var fs = require('fs');
  var glob = require('glob-fs')();
  var path = require('path');
  var result = 0;
  var exclude = ['LICENSE',
    path.join('e2e', 'util', 'db-ca', 'someother-file'),
    path.join('src', 'favicon.ico')];
  var files = [];
  files = glob.readdirSync('**');

  var allFiles = [];

  var patternString = [
    'trade',
    'order',
    'market',
    'securities'
  ];

  files.map((file) => {
    try {
      if (!fs.lstatSync(file).isDirectory() && exclude.indexOf(file) === -1) {
        fs.readFileSync(file).toString().split(/\r?\n/).forEach(function(line){
          patternString.map((pattern) => {
            if (line.indexOf(pattern) !== -1) {
              console.log(file + ' contain `' + pattern + '` in in line "' + line +'";');
              result = 1;
            }
          });
        });
      }
    } catch (e) {
      console.log('Error:', e.stack);
    }
  });
  process.exit(result);

})();

Ответ 28

Я просмотрел все приведенные выше ответы, все они используют стороннюю библиотеку для ее решения. Это простое решение в Node API. например

const fs= require('fs')

let stream = fs.createReadStream('<filename>', { autoClose: true })

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))