Eine Datei zeilenweise in node.js lesen?

553

Ich versuche, eine große Datei zeilenweise zu lesen. Ich habe eine Frage zu Quora gefunden , die sich mit dem Thema befasst, aber mir 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 Bit, das ich herausfinden möchte, ist, wie ich eine Zeile nach der anderen 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 ausführe, über Speicher verfügt.

Alex C.
quelle
2
Dies stellt sich als ziemlich schwierig heraus, wenn nur ein niedriger Pegel verwendet wird fs.readSync(). Sie können binäre Oktette in einen Puffer lesen, aber es gibt keine einfache Möglichkeit, mit partiellen UTF-8- oder UTF-16-Zeichen umzugehen, ohne den Puffer zu überprüfen, bevor Sie ihn in JavaScript-Zeichenfolgen übersetzen und nach EOLs suchen. Der Buffer()Typ verfügt nicht über so viele Funktionen, die auf seinen Instanzen ausgeführt werden können wie native Zeichenfolgen, aber native Zeichenfolgen können keine Binärdaten enthalten. Es scheint mir, dass das Fehlen einer integrierten Methode zum Lesen von Textzeilen aus beliebigen Dateihandles eine echte Lücke in node.js darstellt.
Hippietrail
5
Mit dieser Methode eingelesene leere Zeilen werden in eine Zeile mit einer einzelnen 0 (tatsächlicher Zeichencode für 0) konvertiert. Ich musste diese Zeile dort hacken:if (line.length==1 && line[0] == 48) special(line);
Thabo
2
Man könnte auch das "Zeile für Zeile" -Paket verwenden, das die Arbeit perfekt macht.
Patrice
1
Bitte aktualisieren Sie die Frage, um zu sagen, dass die Lösung darin besteht, einen Transformations-Stream zu verwenden
Gabriel Llamas
2
@DanDascalescu, wenn Sie möchten, können Sie dies zur Liste hinzufügen: Ihr Beispiel landete leicht modifiziert in nodeden API-Dokumenten github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

Antworten:

789

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

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

Oder alternativ:

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 Finale vorhanden ist \n.

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

Dan Dascalescu
quelle
7
Sie benötigen ein Terminal: false in der createInterface-Definition
glasspill
64
Wie bestimme ich die letzte Zeile? Durch das Fangen eines "nahen" Ereignisses:rl.on('close', cb)
Grüner
27
Readline dient einem ähnlichen Zweck wie GNU Readline und nicht zum zeilenweisen Lesen von Dateien. Es gibt einige Einschränkungen beim Lesen von Dateien, und dies ist keine bewährte Methode.
Nakedible
8
@Nakedible: interessant. Könnten Sie eine Antwort mit einer besseren Methode posten?
Dan Dascalescu
6
Ich halte github.com/jahewson/node-byline für die beste Implementierung des zeilenweisen Lesens, aber die Meinungen können variieren.
Nakedible
164

Für eine so einfache Operation sollte keine Abhängigkeit von Modulen von Drittanbietern 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);
});
Kofrasa
quelle
33
Leider funktioniert diese attraktive Lösung nicht richtig - lineEreignisse treten erst nach dem Treffer auf \n, dh alle Alternativen werden übersehen (siehe unicode.org/reports/tr18/#Line_Boundaries ). # 2, Daten nach dem letzten \nwerden stillschweigend ignoriert (siehe stackoverflow.com/questions/18450197/… ). Ich würde diese Lösung als gefährlich bezeichnen, da sie für 99% aller Dateien und für 99% der Daten funktioniert, für den Rest jedoch stillschweigend fehlschlägt . Wann immer Sie dies tun, haben fs.writeFileSync( path, lines.join('\n'))Sie eine Datei geschrieben, die von der obigen Lösung nur teilweise gelesen wird.
Flow
4
Bei dieser Lösung liegt ein Problem vor. Wenn Sie your.js <lines.txt verwenden, erhalten Sie keine letzte Zeile. Wenn es am Ende natürlich kein '\ n' gibt.
zag2art
Das readlinePaket verhält sich für einen erfahrenen Unix / Linux-Programmierer wirklich bizarr.
Pointy
11
rd.on("close", ..);kann als Rückruf verwendet werden (tritt auf, wenn alle Zeilen gelesen werden)
Luca Steeb
6
Das Problem "Daten nach dem letzten \ n" scheint in meiner Version von Node (0.12.7) behoben zu sein. Deshalb bevorzuge ich diese Antwort, die am einfachsten und elegantesten erscheint.
Myk Melez
63

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

fs.createReadStream

Übergeben Sie dann diesen Stream an Lazy

Raynos
quelle
2
Gibt es so etwas wie ein Endereignis für Lazy? Wenn alle Zeilen eingelesen wurden?
Max
1
@ Max, versuchen Sie:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi
6
@Cecchi und @Max verwenden keinen Join, da dadurch die gesamte Datei im Speicher gepuffert wird. Hören Sie sich stattdessen einfach das "End" -Ereignis an:new lazy(...).lines.forEach(...).on('end', function() {...})
Corin
3
@Cecchi, @Corin und @Max: Für was es wert ist , fuhr ich mich verrückt Verkettungs .on('end'... nach .forEach(...) , wenn in der Tat verhielten sich alles wie erwartet funktioniert, wenn ich das Ereignis gebunden zuerst .
Crowjonah
52
Dieses Ergebnis ist in den Suchergebnissen sehr hoch, daher ist es erwähnenswert, dass Lazy verlassen aussieht. Es sind 7 Monate ohne Änderungen vergangen und es gibt einige schreckliche Fehler (letzte Zeile ignoriert, massive Speicherlecks usw.).
Blu
38

Es gibt ein sehr schönes Modul zum zeilenweisen Lesen einer Datei, es heißt Zeilenleser

damit schreibst du einfach:

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 Schnittstelle im Java-Stil iterieren, wenn Sie mehr Kontrolle benötigen:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});
Polaretto
quelle
4
Das funktioniert gut. Es liest sogar die letzte Zeile (!). Es ist erwähnenswert, dass das \ r beibehalten wird, wenn es sich um eine Textdatei im Windows-Stil handelt. line.trim () entfernt das Extra \ r.
Pierre-Luc Bertrand
Es ist nicht optimal, dass die Eingabe nur aus einer benannten Datei stammen kann und nicht (für ein offensichtliches und äußerst wichtiges Beispiel process/stdin). Zumindest wenn es möglich ist, ist es sicherlich nicht offensichtlich, wenn man den Code liest und versucht.
Pointy
2
In der Zwischenzeit gibt es eine integrierte Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
Dies ist alt, aber für den Fall, dass jemand darauf stößt: function(reader)und function(line)sollte sein: function(err,reader)und function(err,line).
Jallmer
1
line-readerLiest die Datei nur für den Datensatz asynchron. Die synchrone Alternative dazu istline-reader-sync
Prajwal Dhatwalia
30
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})
John Williams
quelle
42
Dadurch wird die gesamte Datei im Speicher gelesen und dann in Zeilen aufgeteilt. Es ist nicht das, was die Fragen stellen. Es geht darum, große Dateien bei Bedarf nacheinander lesen zu können.
Dan Dascalescu
2
Dies passt zu meinem Anwendungsfall. Ich suchte nach einer einfachen Möglichkeit, Eingaben von einem Skript in ein anderes Format zu konvertieren. Vielen Dank!
Callat
23

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();
Leitender Entwickler
quelle
Diese Antwort ist viel besser als alles andere, dank ihres auf Versprechungen basierenden Verhaltens, das den EOF eindeutig anzeigt.
Phil294
Danke, das ist süß.
Goran Stoyanov
3
Vielleicht ist dies für andere offensichtlich, aber das Debuggen hat eine Weile gedauert: Wenn awaitzwischen dem createInterface()Aufruf und dem Start der for awaitSchleife ein s liegt , verlieren Sie auf mysteriöse Weise Zeilen vom Anfang der Datei an. createInterface()Sofort werden hinter den Kulissen Zeilen ausgegeben, und der implizit erstellte asynchrone Iterator const line of rlkann erst dann auf diese Zeilen warten , wenn sie erstellt wurden.
Andrewdotn
19

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.

nf071590
quelle
2
Wenn Sie readline is not definedoder erhalten fs is not defined, fügen Sie hinzu var readline = require('readline');und var fs = require('fs');damit dies funktioniert. Ansonsten süßer, süßer Code. Vielen Dank.
bergie3000
12
Diese Antwort ist eine exakte Kopie einer früheren Antwort , aber ohne die Warnung vor Kommentaren ist das Readline-Paket als instabil markiert (Stand April 2015 immer noch instabil) und hatte Mitte 2013 Probleme beim Lesen der letzten Zeilen einer Datei ohne Zeilenenden . Das letzte Zeilenproblem trat auf, als ich es zum ersten Mal in Version 0.10.35 verwendete, und ging dann weg. / Argh
Ruffin
Sie müssen die Ausgabe nicht angeben, wenn Sie nur aus einem Dateistream lesen .
Dan Dascalescu
18

Sie können jederzeit Ihren eigenen Zeilenleser rollen. Ich habe dieses Snippet noch nicht bewertet, aber es teilt den eingehenden 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 habe mir das ausgedacht, als ich an einem schnellen Protokollanalyse-Skript gearbeitet habe, das Daten während der Protokollanalyse sammeln musste, und ich fand es schön, dies mit js und node anstelle von perl oder bash zu versuchen.

Wie auch immer, ich bin der Meinung, dass kleine NodeJS-Skripte in sich geschlossen sein sollten und sich nicht auf Module von Drittanbietern stützen sollten. Nachdem Sie alle Antworten auf diese Frage gelesen haben und jeweils verschiedene Module für die Zeilenanalyse verwenden, könnte eine native 13 SLOC-NodeJS-Lösung von Interesse sein.

Ernelli
quelle
Es scheint keine triviale Möglichkeit zu geben, dies auf beliebige Dateien auszudehnen, außer nur stdin... es sei denn, ich vermisse etwas.
Hippietrail
3
@ Hippietrail Sie können ein ReadStreammit erstellen fs.createReadStream('./myBigFile.csv')und es anstelle vonstdin
Nolith
2
Enthält jeder Block garantiert nur vollständige Zeilen? Werden Multi-Byte-UTF-8-Zeichen garantiert nicht an Blockgrenzen aufgeteilt?
Hippietrail
1
@hippietrail Ich glaube nicht, dass Multibyte-Zeichen von dieser Implementierung korrekt behandelt werden. Dazu muss man zuerst die Puffer korrekt in Strings konvertieren und die Zeichen verfolgen, die zwischen zwei Puffern aufgeteilt sind. Um das richtig zu machen, kann man den eingebauten StringDecoder
Ernelli
In der Zwischenzeit gibt es eine integrierte Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
12

Mit dem Trägermodul :

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});
Touv
quelle
Nett. Dies funktioniert auch für jede Eingabedatei: var inStream = fs.createReadStream('input.txt', {flags:'r'}); Ihre Syntax ist jedoch sauberer als die dokumentierte Methode zur Verwendung von .on ():carrier.carry(inStream).on('line', function(line) { ...
Brent Faust
Träger scheint nur Endungen zu handhaben \r\nund \nzu leiten. Wenn Sie jemals mit Testdateien im MacOS-Stil vor OS X arbeiten müssen, werden diese verwendet, \rund Carrier übernimmt dies nicht. Überraschenderweise schweben immer noch solche Dateien in freier Wildbahn herum. Möglicherweise müssen Sie auch die Unicode-Stückliste (Byte Order Mark) explizit behandeln. Diese wird am Anfang von Textdateien im Einflussbereich von MS Windows verwendet.
Hippietrail
In der Zwischenzeit gibt es eine integrierte Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
9

Am Ende hatte ich einen massiven Speicherverlust, bei dem Lazy zeilenweise gelesen wurde, als ich versuchte, diese Zeilen zu verarbeiten und in einen anderen Stream zu schreiben, da Drain / Pause / Resume im Knoten funktioniert (siehe: http: // elegantcode) .com / 2011/04/06 / Baby-Schritte-mit-Knoten-js-pumpen-Daten-zwischen-Streams / (ich liebe diesen Kerl übrigens)). Ich habe Lazy nicht genau genug angesehen, um genau zu verstehen, warum, aber ich konnte meinen Lesestream nicht anhalten, um einen Abfluss zu ermöglichen, ohne dass Lazy austrat.

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 Lazy Line ausführen, ist dies undicht. Die neueste Version leckt überhaupt nicht und Sie können sie wahrscheinlich als Grundlage für einen Leser / Prozessor verwenden. Obwohl ich einige benutzerdefinierte Sachen drin habe.

Bearbeiten: Ich denke, ich sollte auch beachten, dass mein Code mit Lazy gut funktioniert hat, bis ich festgestellt habe, dass ich ausreichend große XML-Fragmente schreibe, die aufgrund einer Notwendigkeit entleeren / pausieren / wieder aufnehmen. Für kleinere Stücke war es in Ordnung.

j03m
quelle
In der Zwischenzeit gibt es eine viel einfachere Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
Jep. Das ist jetzt der richtige Weg. Aber das war von 2011. :)
j03m
8

Bearbeiten:

Verwenden Sie einen Transformations-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 ();
Gabriel Llamas
quelle
1
In der Zwischenzeit gibt es eine viel einfachere Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
7

Seit ich meine ursprüngliche Antwort gepostet habe, habe ich festgestellt, dass Split ein sehr einfach zu verwendendes Knotenmodul zum Lesen von Zeilen in einer Datei ist. Welches akzeptiert auch optionale Parameter.

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

Ich habe nicht an sehr großen Dateien getestet. Lassen Sie es uns wissen, wenn Sie dies tun.

nf071590
quelle
6

Ich war frustriert über das Fehlen einer umfassenden Lösung dafür und stellte meinen eigenen Versuch zusammen ( git / npm ). Liste der Funktionen kopiert:

  • Interaktive Leitungsverarbeitung (Callback-basiert, kein Laden der gesamten Datei in den RAM)
  • Optional können Sie alle Zeilen in einem Array zurückgeben (Detail- oder Rohmodus).
  • Unterbrechen Sie das Streaming interaktiv oder führen Sie eine karten- / filterähnliche Verarbeitung durch
  • Erkennen Sie alle Newline-Konventionen (PC / Mac / Linux).
  • Richtige Behandlung der Eof / Last Line
  • Korrekter Umgang mit Multi-Byte-UTF-8-Zeichen
  • Abrufen von Informationen zum Byte-Offset und zur Bytelänge pro Zeile
  • Direktzugriff mit zeilenbasierten oder bytebasierten Offsets
  • Ordnen Sie Informationen zum Zeilenversatz automatisch zu, um den Direktzugriff zu beschleunigen
  • Keine Abhängigkeiten
  • Tests

NIH? Du entscheidest :-)

panta82
quelle
5
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("++++++++++++++++++++")
})
user531097
quelle
Ich werde das testen, aber können Sie mir sagen, ist es garantiert, dass niemals Multibyte-Zeichen gebrochen werden? (UTF-8 / UTF-16)
Hippietrail
2
@hippietrail: Die Antwort lautet Nein für UTF-8, obwohl es eher an einem Byte-Stream als an einem Zeichen-Stream arbeitet. Es bricht in Zeilenumbrüchen (0x0a). In UTF-8 ist für alle Bytes eines Multibyte-Zeichens das Bit höherer Ordnung gesetzt. Somit kann kein Multibyte-Zeichen eine eingebettete Newline oder ein anderes allgemeines ASCII-Zeichen enthalten. UTF-16 und UTF-32 sind jedoch eine andere Sache.
George
@ George: Ich denke wir verstehen uns falsch. Da CR und LF beide im ASCII-Bereich liegen und UTF-8 die 128 ASCII-Zeichen unverändert beibehält, können weder CR noch LF jemals Teil eines Multibyte-UTF-8-Zeichens sein. Was ich gefragt habe, ist, ob das datain dem Aufruf, stream.on("data")jemals mit nur einem Teil eines Multibyte-UTF-8-Zeichens zu beginnen oder zu enden, wie es U+10D0aus den drei Bytes bestehte1 83 90
Hippietrail
1
Dadurch wird weiterhin der gesamte Dateiinhalt in den Speicher geladen, bevor eine "neue Zeile" erstellt wird. Dies liest nicht eine Zeile nach der anderen, sondern nimmt ALLE Zeilen und teilt sie dann entsprechend der Pufferlänge "neue Zeile" auf. Diese Methode macht den Zweck des Erstellens eines Streams zunichte.
Justin
In der Zwischenzeit gibt es eine viel einfachere Möglichkeit, Zeilen aus einer Datei mithilfe des readlineKernmoduls zu lesen .
Dan Dascalescu
5

Ich wollte das gleiche Problem angehen, im Grunde genommen, was in Perl sein würde:

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

Mein Anwendungsfall war nur ein eigenständiges Skript, kein Server, daher war die Synchronisierung in Ordnung. Das waren meine Kriterien:

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

Dies ist ein Projekt für mich, um ein Gefühl für einfachen Skriptcode in node.js zu bekommen und zu entscheiden, wie nützlich er als Ersatz für andere Skriptsprachen wie Perl ist.

Nach überraschend viel Aufwand und ein paar Fehlstarts ist dies der Code, den ich mir ausgedacht habe. Es ist ziemlich schnell, aber weniger trivial als ich erwartet hätte: (Gabel auf 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.

Hippietrail
quelle
5

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)
});
Dorian
quelle
2

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

  });
});
Neurosnap
quelle
2

Wenn Sie eine Datei Zeile für Zeile lesen und in eine andere schreiben möchten:

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');
   });
};
Thami Bouchnafa
quelle
Was ist der Unterschied zwischen deiner und Kofrasas Antwort?
Buffalo
2
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 fand die obige Lösung, die anderen ähnlich sieht, aber aSync ist und große Dateien sehr schnell lesen kann

Hoffe das hilft

user2056154
quelle
1

Ich habe ein kleines Modul, das dies gut kann und von einigen anderen Projekten verwendet wird. Npm readline Beachten Sie, dass es in Knoten v10 ein natives Readline-Modul gibt, sodass ich mein Modul als linebyline https://www.npmjs.com/package/ erneut veröffentlicht habe. Zeile für Zeile

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

Eine andere Lösung besteht darin, die Logik über den sequentiellen Executor nsynjs auszuführen . Es liest Dateien zeilenweise mit dem Knoten-Readline-Modul und verwendet keine Versprechen oder Rekursionen, sodass es bei großen Dateien nicht fehlschlägt. So sieht der Code aus:

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

amaksr
quelle
1

Zwei Fragen, die wir uns bei solchen Operationen stellen müssen, sind:

  1. Wie viel Speicher wird dafür verwendet?
  2. Steigt der Speicherverbrauch mit der Dateigröße drastisch an?

Lösungen wie require('fs').readFileSync()lädt die gesamte Datei in den Speicher. Dies bedeutet, dass die zum Ausführen von Vorgängen erforderliche Speichermenge fast der Dateigröße entspricht. Wir sollten diese für etwas Größeres als vermeiden50mbs

Wir können die von einer Funktion verwendete Speichermenge leicht verfolgen, indem wir diese Codezeilen nach dem Funktionsaufruf platzieren:

    const used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(
      `The script uses approximately ${Math.round(used * 100) / 100} MB`
    );

Derzeit können Sie bestimmte Zeilen am besten aus einer großen Datei lesen, indem Sie die Readline des Knotens verwenden . Die Dokumentation enthält erstaunliche Beispiele .

Obwohl wir dafür kein Modul eines Drittanbieters benötigen. Wenn Sie jedoch einen Unternehmenscode schreiben, müssen Sie viele Randfälle behandeln. Ich musste ein sehr leichtes Modul namens Apick File Storage schreiben , um all diese Randfälle zu behandeln.

Apick-Dateispeichermodul: https://www.npmjs.com/package/apickfs Dokumentation: https://github.com/apickjs/apickFS#readme

Beispieldatei: https://1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx

Beispiel: Modul installieren

npm i apickfs
// import module
const apickFileStorage = require('apickfs');
//invoke readByLineNumbers() method
apickFileStorage
  .readByLineNumbers(path.join(__dirname), 'big.txt', [163845])
  .then(d => {
    console.log(d);
  })
  .catch(e => {
    console.log(e);
  });

Diese Methode wurde erfolgreich mit bis zu 4 GB dichten Dateien getestet.

big.text ist eine dichte Textdatei mit 163.845 Zeilen und einer Größe von 124 MB. Das Skript zum Lesen von 10 verschiedenen Zeilen aus dieser Datei verwendet nur ungefähr 4,63 MB Speicher. Und es analysiert gültiges JSON kostenlos in Objekte oder Arrays. 🥳 Super !!

Wir können eine einzelne Zeile der Datei oder Hunderte von Zeilen der Datei mit sehr geringem Speicherverbrauch lesen.

vivek agarwal
quelle
0

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 in einem Stream und hören Sie sich die Zeilenereignisse an, die ausgegeben werden.

GR-

Elmer
quelle
0

Während Sie das readlineModul wahrscheinlich verwenden sollten, wie in der oberen Antwort angegeben, readlinescheint es eher auf Befehlszeilenschnittstellen als auf das Lesen von Zeilen ausgerichtet zu sein. Es ist auch ein bisschen undurchsichtiger in Bezug auf die Pufferung. (Jeder, der einen Streaming-Line-orientierten Reader benötigt, wird wahrscheinlich die Puffergröße anpassen wollen). Das Readline-Modul besteht aus ~ 1000 Zeilen, während dieses mit Statistiken und Tests 34 Zeilen beträgt.

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 Statistiken 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);
        });
    }
}
Javajosh
quelle
0
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>';


});
Arindam
quelle
0

Ich verpacke die gesamte Logik der täglichen Zeilenverarbeitung als npm-Modul: 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`)})

Joyer
quelle
-1

Ich benutze unten Code die gelesenen Zeilen, nachdem ich überprüft habe, dass es kein Verzeichnis ist und es nicht in der Liste der Dateien enthalten ist, muss nicht überprüft werden.

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

})();
Aniruddha Das
quelle
-1

Ich habe alle oben genannten Antworten durchgesehen, alle verwenden eine Bibliothek von Drittanbietern, um sie zu lösen. Es gibt eine einfache Lösung in der Node-API. z.B

const fs= require('fs')

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

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