webentwicklung-frage-antwort-db.com.de

Eine Datei Zeile für Zeile in node.js lesen

Ich versuche, eine große Datei Zeile für Zeile zu lesen. Ich fand eine Frage zu Quora , die sich mit dem Thema befasste, aber es fehlen einige Verbindungen, um das Ganze zusammenzufügen.

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

Das bisschen, das ich herausfinden möchte, ist, wie ich zeilenweise eine Datei aus einer Datei anstelle von STDIN wie in diesem Beispiel lesen kann.

Ich habe es versucht: 

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

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

aber es funktioniert nicht Ich weiß, dass ich zur Not auf etwas wie PHP zurückgreifen könnte, aber ich würde das gerne herausfinden. 

Ich glaube nicht, dass die andere Antwort funktionieren würde, da die Datei viel größer ist als der Server, auf dem ich sie ausgeführt habe, über Speicher verfügt. 

462
Alex C

Seit Node.js v0.12 und ab Node.js v4.0.0 gibt es ein stabiles readline core-Modul. Es ist der einfachste Weg, Zeilen aus einer Datei ohne externe Module zu lesen:

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

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

Die letzte Zeile wird korrekt gelesen (ab Node v0.12 oder höher), auch wenn kein endgültiger \n vorhanden ist.

UPDATE: Dieses Beispiel wurde der offiziellen API-Dokumentation von Node hinzugefügt .

632
Dan Dascalescu

Für eine solche einfache Operation sollte keine Abhängigkeit von Fremdmodulen bestehen. Geh einfach.

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);
});
153
kofrasa

Sie müssen die Datei nicht open, sondern eine ReadStream erstellen.

fs.createReadStream

Dann übergeben Sie diesen Stream an Lazy

63
Raynos

es gibt ein sehr schönes Modul, um eine Datei Zeile für Zeile zu lesen. Es heißt line-reader

damit schreiben Sie einfach nur:

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's the last one
  }
});

sie können die Datei sogar mit einer "Java-Stil" -Schnittstelle iterieren, wenn Sie mehr Kontrolle benötigen:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});
33
polaretto
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})
20
John Williams

Altes Thema, aber das funktioniert:

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

Einfach. Kein externes Modul erforderlich.

18
nf071590

Sie können immer Ihren eigenen Zeilenleser rollen. Ich habe dieses Snippet noch nicht getestet, aber es teilt den ankommenden Stream von Chunks korrekt in Zeilen ohne das nachfolgende '\ n' auf.

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

Ich kam dazu, als ich an einem schnellen Log-Parsing-Skript arbeitete, das während des Log-Parsings Daten sammeln musste, und ich dachte, es wäre schön, dies mit js und node anstelle von Perl oder bash zu versuchen.

Ich denke jedoch, dass kleine nodejs-Skripts in sich geschlossen sein sollten und nicht auf Modulen von Drittanbietern angewiesen sind. Wenn Sie also alle Antworten auf diese Frage gelesen haben und jeweils verschiedene Module für das Zeilen-Parsing verwenden, könnte eine native SLOC-Lösung mit 13 SLOC von Interesse sein.

18
Ernelli

Mit dem Trägermodul :

var carrier = require('carrier');

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

Bearbeiten:

Verwenden Sie einen transform stream .


Mit einem BufferedReader können Sie Zeilen lesen.

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 ();
8
Gabriel Llamas

Am Ende kam es zu einem massiven, massiven Speicherverlust. Ich habe Lazy verwendet, um Zeile für Zeile zu lesen, wenn ich dann versuche, diese Zeilen zu verarbeiten und sie in einen anderen Stream zu schreiben, da Drain/Pause/Resume im Knoten funktioniert (siehe: http://elegantcode.com/2011/04/06/taking-baby-steps-mit-node-js-pumping-data-between-streams/ (ich liebe diesen Kerl übrigens)). Ich habe Lazy nicht genau genug angesehen, um genau zu verstehen, warum, aber ich konnte meinen Lesestrom nicht unterbrechen, um einen Abfluss zuzulassen, ohne dass Lazy beendet wurde.

Ich habe den Code geschrieben, um massive CSV-Dateien in XML-Dokumente zu verarbeiten. Den Code finden Sie hier: https://github.com/j03m/node-csv2xml

Wenn Sie die vorherigen Revisionen mit der Lazy-Zeile ausführen, tritt ein Leck auf. Die neueste Version tritt nicht aus, und Sie können sie wahrscheinlich als Grundlage für einen Leser/Prozessor verwenden. Obwohl ich ein paar benutzerdefinierte Sachen habe. 

Edit: Ich denke, ich sollte auch beachten, dass mein Code mit Lazy gut funktioniert hat, bis ich fand, dass ich groß genug XML-Fragmente schrieb, die aus Notwendigkeit abtropfen/pausieren/wieder aufnehmen. Für kleinere Brocken war es gut.

8
j03m

In den meisten Fällen sollte dies ausreichen:

const fs = require("fs")

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

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

Ich war frustriert über das Fehlen einer umfassenden Lösung, daher habe ich meinen eigenen Versuch zusammengestellt ( git / npm ). Kopierte Liste der Features:

  • Interaktive Zeilenverarbeitung (Callback-basiert, kein Laden der gesamten Datei in den RAM)
  • Optional können Sie alle Zeilen in einem Array zurückgeben (detaillierter oder unformatierter Modus).
  • Interaktiv unterbrechen Sie das Streaming oder führen Sie eine Map/Filter-ähnliche Verarbeitung durch
  • Jede Newline-Konvention erkennen (PC/Mac/Linux)
  • Richtige eof/last line-Behandlung
  • Korrekter Umgang mit Multi-Byte-UTF-8-Zeichen
  • Rufen Sie Byteversatz und Bytelängeninformationen pro Zeile ab
  • Direktzugriff über linien- oder bytebasierte Offsets
  • Kennzeichnen Sie automatisch Linienversatzinformationen, um den Direktzugriff zu beschleunigen
  • Null Abhängigkeiten
  • Tests

NIH? Du entscheidest :-)

6
panta82

Seit ich meine ursprüngliche Antwort veröffentlicht habe, habe ich festgestellt, dass split ein sehr einfach zu verwendendes Knotenmodul zum Lesen von Zeilen in einer Datei ist. Die auch optionale Parameter akzeptiert. 

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

Wurde nicht mit sehr großen Dateien getestet. Lass es uns wissen.

6
nf071590
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("++++++++++++++++++++")
})
5
user531097

Ich wollte das gleiche Problem lösen, was im Grunde in Perl wäre:

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

Mein Anwendungsfall war nur ein eigenständiges Skript, kein Server, so synchron war alles in Ordnung. Dies waren meine Kriterien:

  • Der minimale synchrone Code, der in vielen Projekten wiederverwendet werden kann.
  • Keine Begrenzung der Dateigröße oder der Zeilenanzahl.
  • Keine Begrenzung der Zeilenlänge.
  • Kann in UTF-8 mit vollem Unicode umgehen, einschließlich Zeichen außerhalb des BMP.
  • Kann mit * Nix- und Windows-Zeilenenden umgehen (Mac im alten Stil wird für mich nicht benötigt).
  • Zeilenende-Zeichen, die in Zeilen enthalten sein sollen.
  • Kann letzte Zeile mit oder ohne Zeilenendezeichen behandeln.
  • Verwenden Sie keine externen Bibliotheken, die nicht in der node.js-Distribution enthalten sind.

Dies ist ein Projekt für mich, um ein Gefühl für den Code-Code der unteren Ebene in node.js zu bekommen und zu entscheiden, wie praktikabel es ist, um andere Skriptsprachen wie Perl zu ersetzen.

Nach einem überraschenden Aufwand und einigen Fehlstarts ist dies der Code, den ich mir ausgedacht habe. Es ist ziemlich schnell, aber weniger trivial als ich erwartet hätte: (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;
    }
  }
}

Es könnte wahrscheinlich weiter aufgeräumt werden, es war das Ergebnis von Versuch und Irrtum.

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

Ich hatte das gleiche Problem und kam zu der oben genannten Lösung. __ sieht ähnlich aus, ist aber aSync und kann große Dateien sehr schnell lesen

Hoffentlich hilft das

2
user2056154

Generatorenbasierter Zeilenleser: 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
neurosnap

Wenn Sie eine Datei Zeile für Zeile lesen und diese in eine andere schreiben wollen: 

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

Eine andere Lösung besteht darin, die Logik über den sequentiellen Executor nsynjs auszuführen. Er liest die Datei Zeile für Zeile mit dem Node-Readline-Modul und verwendet keine Versprechungen oder Rekursionen. Daher wird bei großen Dateien kein Fehler auftreten. So wird der Code aussehen:

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

Der obige Code basiert auf diesem Beispiel: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js

1
amaksr

Update im Jahr 2019

Ein großartiges Beispiel ist bereits in der offiziellen Nodejs-Dokumentation veröffentlicht. Hier

Dies setzt voraus, dass die neuesten Nodejs auf Ihrem Computer installiert sind. > 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();
1
Lead Developer

Ich habe ein kleines Modul, das dies gut macht und von vielen anderen Projekten verwendet wird npm readline Hinweis Wenn im Knoten v10 ein natives Readline-Modul vorhanden ist, habe ich mein Modul erneut als linebyline https: // www veröffentlicht. npmjs.com/package/linebyline

wenn Sie das Modul nicht verwenden möchten, ist die Funktion sehr einfach:

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);
1
Maleck13

Ich füge die gesamte Logik der täglichen Zeilenverarbeitung als npm-Modul zusammen: line-kithttps://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`)})

0
Joyer
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>';


});
0
Arindam

ich benutze das:

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

verwenden Sie diese Funktion für einen Stream und überwachen Sie die Zeilenereignisse, die ausgegeben werden.

gR-

0
Elmer

Während Sie wahrscheinlich das Modul readline verwenden sollten, wie die erste Antwort nahelegt, scheint readline eher auf Befehlszeilenschnittstellen als auf das Lesen von Zeilen ausgerichtet zu sein. Es ist auch etwas undurchsichtiger in Bezug auf die Pufferung. (Wer ein Streaming-Line-orientiertes Lesegerät benötigt, wird wahrscheinlich die Puffergröße anpassen wollen). Das Readline-Modul ist ~ 1000 Zeilen, während dies bei Statistiken und Tests 34 ist.

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

Hier ist eine noch kürzere Version ohne Statistik mit 19 Zeilen:

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);
        });
    }
}
0
javajosh