node.js: Liest eine Textdatei in ein Array. (Jede Zeile ist ein Element im Array.)

163

Ich möchte eine sehr, sehr große Datei in ein JavaScript-Array in node.js lesen.

Also, wenn die Datei so ist:

first line
two 
three
...
...

Ich hätte das Array:

['first line','two','three', ... , ... ] 

Die Funktion würde folgendermaßen aussehen:

var array = load(filename); 

Daher ist die Idee, alles als Zeichenfolge zu laden und dann zu teilen, nicht akzeptabel.

Chacko
quelle
Diese Frage muss ernsthaft bearbeitet und bereinigt werden. Es heißt , eine Textdatei in ein Array lesen, aber wenn Sie alle Antworten und Kommentare lesen, bedeutet dies wirklich, dass Sie eine Textdatei zeilenweise lesen . Auf diese Frage hat @zswang die bisher beste Antwort.
Jess
yup lesen Sie einfach diese Datei und schieben Sie jede Zeile in ein Array: stackoverflow.com/a/34033928/1536309
Blair Anderson

Antworten:

89

Wenn Sie die endgültigen Daten in ein Array einpassen können, können Sie sie dann nicht auch in eine Zeichenfolge einfügen und aufteilen, wie vorgeschlagen wurde? Wenn Sie die Datei zeilenweise verarbeiten möchten, können Sie in jedem Fall auch Folgendes versuchen:

var fs = require('fs');

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    while (index > -1) {
      var line = remaining.substring(0, index);
      remaining = remaining.substring(index + 1);
      func(line);
      index = remaining.indexOf('\n');
    }
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

function func(data) {
  console.log('Line: ' + data);
}

var input = fs.createReadStream('lines.txt');
readLines(input, func);

BEARBEITEN: (als Antwort auf einen Kommentar von phopkins ) Ich denke, dass (zumindest in neueren Versionen) Teilzeichenfolgen keine Daten kopieren, sondern ein spezielles SlicedString-Objekt erstellen (aus einem kurzen Blick auf den v8-Quellcode). In jedem Fall handelt es sich hier um eine Modifikation, die den erwähnten Teilstring vermeidet (getestet an einer Datei im Wert von mehreren Megabyte im Wert von "Alle Arbeit und kein Spiel machen Jack zu einem langweiligen Jungen"):

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    var last  = 0;
    while (index > -1) {
      var line = remaining.substring(last, index);
      last = index + 1;
      func(line);
      index = remaining.indexOf('\n', last);
    }

    remaining = remaining.substring(last);
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}
mtomis
quelle
Vielen Dank. Um Ihre Frage zu beantworten: Nein, die Zeichenfolge wäre zu groß.
Chacko
7
Ich habe dies bei Dateien mit etwa 2 MB versucht und es war schmerzhaft langsam, viel langsamer als das synchrone Einlesen der Dateien in eine Zeichenfolge. Ich denke, das Problem ist die verbleibende = verbleibende Zeichenfolge. Die "Daten" des Knotens geben Ihnen möglicherweise gleichzeitig viel, und das Kopieren für jede Zeile wird schnell zu O (n ^ 2).
Fiona Hopkins
@ Finbars Antwort ist viel besser
rü-
442

Synchron:

var fs = require('fs');
var array = fs.readFileSync('file.txt').toString().split("\n");
for(i in array) {
    console.log(array[i]);
}

Asynchron:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
});
Finbarr
quelle
11
Vielen Dank. Leider musste ich meine Frage bearbeiten. Ich meine, wie man eine sehr große Datei liest. Es ist nicht akzeptabel, alles in einer Zeichenfolge zu lesen.
Chacko
1
Genau das, was ich brauchte. Einfach und schnell.
Hcabnettek
16
Ich fand dies bei einer Datei, die von Windows erstellt wurde. Ich musste \ r \ n teilen, aber das hat Macs kaputt gemacht. so eine robustere; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); arbeitete für beide
Will Hancock
6
+1 Es gibt ein Problem im Stackoverflow. Jetzt finde ich oft hoch bewertete Antworten, nachdem ich zu weit nach unten gescrollt habe. Dies ist auch ein Beispiel dafür. Es hat die höchste Stimmenzahl, befindet sich jedoch ganz unten auf der Seite. Ich denke, Stackoverflow muss seinen Bestellalgorithmus verbessern.
Shashwat
1
@shashwat Die Person, die die Frage stellt, kann entscheiden, welche die richtige Antwort ist. In diesem Fall benötigten sie eine Streaming-Lösung für große Dateien, und es ist nicht akzeptabel, die gesamte Datei in eine Zeichenfolge einzufügen. Mit SO ist eigentlich nichts falsch.
Legalisieren Sie den
73

Unter Verwendung des Node.js Readline- Modul .

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

var filename = process.argv[2];
readline.createInterface({
    input: fs.createReadStream(filename),
    terminal: false
}).on('line', function(line) {
   console.log('Line: ' + line);
});
zswang
quelle
1
Leider gibt es ein Problem mit dieser Lösung: Sie erhalten keine letzte Zeile, wenn die Datei \nam Ende keine hat ! Siehe: stackoverflow.com/questions/18450197/…
Yves M.
8
Knoten hat dieses Problem mit dem \ n stackoverflow.com/a/32599033/3763850
Gemtastic
14

js:

var array = fs.readFileSync('file.txt', 'utf8').split('\n');

ts:

var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Hojin
quelle
1
Um zu verhindern, dass die oben genannten TypeError: fs.readFileSync(...).split is not a functionPunkte ausgelöst werden, sollten Sie .toString () wie var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
folgt verwenden
11

Verwenden Sie readline ( Dokumentation ). Hier ist ein Beispiel, in dem Sie eine CSS-Datei lesen, nach Symbolen analysieren und diese in json schreiben

var results = [];
  var rl = require('readline').createInterface({
    input: require('fs').createReadStream('./assets/stylesheets/_icons.scss')
  });


  // for every new line, if it matches the regex, add it to an array
  // this is ugly regex :)
  rl.on('line', function (line) {
    var re = /\.icon-icon.*:/;
    var match;
    if ((match = re.exec(line)) !== null) {
      results.push(match[0].replace(".",'').replace(":",''));
    }
  });


  // readline emits a close event when the file is read.
  rl.on('close', function(){
    var outputFilename = './icons.json';
    fs.writeFile(outputFilename, JSON.stringify(results, null, 2), function(err) {
        if(err) {
          console.log(err);
        } else {
          console.log("JSON saved to " + outputFilename);
        }
    });
  });
Blair Anderson
quelle
6

file.linesmit JFile-Paket

Pseudo

var JFile=require('jfile');

var myF=new JFile("./data.txt");
myF.lines // ["first line","second line"] ....

Vergiss vorher nicht:

npm install jfile --save
Abdennour TOUMI
quelle
5

Mit einem BufferedReader sollte die Funktion jedoch asynchron sein:

var load = function (file, cb){
    var lines = [];
    new BufferedReader (file, { encoding: "utf8" })
        .on ("error", function (error){
            cb (error, null);
        })
        .on ("line", function (line){
            lines.push (line);
        })
        .on ("end", function (){
            cb (null, lines);
        })
        .read ();
};

load ("file", function (error, lines){
    if (error) return console.log (error);
    console.log (lines);
});
Gabriel Llamas
quelle
4

Ich möchte nur @finbarr großartige Antwort hinzufügen, eine kleine Korrektur im asynchronen Beispiel:

Asynchron:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
    done();
});

@MadPhysicist, done () gibt den Async frei. Anruf.

HernanFila
quelle
3

Dies ist eine Variation der obigen Antwort von @mtomis.

Es entsteht ein Linienstrom. Es gibt "Daten" - und "End" -Ereignisse aus, sodass Sie das Ende des Streams verwalten können.

var events = require('events');

var LineStream = function (input) {
    var remaining = '';

    input.on('data', function (data) {
        remaining += data;
        var index = remaining.indexOf('\n');
        var last = 0;
        while (index > -1) {
            var line = remaining.substring(last, index);
            last = index + 1;
            this.emit('data', line);
            index = remaining.indexOf('\n', last);
        }
        remaining = remaining.substring(last);
    }.bind(this));

    input.on('end', function() {
        if (remaining.length > 0) {
            this.emit('data', remaining);
        }
        this.emit('end');
    }.bind(this));
}

LineStream.prototype = new events.EventEmitter;

Verwenden Sie es als Wrapper:

var lineInput = new LineStream(input);

lineInput.on('data', function (line) {
    // handle line
});

lineInput.on('end', function() {
    // wrap it up
});
oferei
quelle
1
Am Ende werden Ereignisse von Instanzen gemeinsam genutzt. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP
Über welche Fälle sprechen Sie genau?
oferei
1
Versuchen Sie zu erstellen var li1 = new LineStream(input1), li2 = new LineStream(input2);und zählen Sie dann, wie oft 'Ende' für jedes
ausgelöst wird
Ich habe es versucht. 'end' wurde für jede Instanz einmal ausgelöst. var fs = require('fs'); var input1 = fs.createReadStream('text.txt'); var ls1 = new LineStream(input1); ls1.on('data', function (line) { console.log('1:line=' + line); }); ls1.on('end', function (line) { console.log('1:fin'); }); var input2 = fs.createReadStream('text.txt'); var ls2 = new LineStream(input2); ls2.on('data', function (line) { console.log('2:line=' + line); }); ls2.on('end', function (line) { console.log('2:fin'); }); Ausgabe: Jede Zeile in der Textdatei wurde für jede Instanz einmal ausgelöst. so war "Ende".
oferei
2

Ich hatte das gleiche Problem und habe es mit dem Modul Zeile für Zeile gelöst

https://www.npmjs.com/package/line-by-line

Zumindest für mich funktioniert es wie ein Zauber, sowohl im synchronen als auch im asynchronen Modus.

Außerdem kann das Problem mit nicht terminierenden Leitungen \ n mit der folgenden Option gelöst werden:

{ encoding: 'utf8', skipEmptyLines: false }

Synchrone Verarbeitung von Zeilen:

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) {
    // 'line' contains the current line without the trailing newline character.
});

lr.on('end', function () {
    // All lines are read, file is closed now.
}); 
Antoni
quelle
2

Die Verwendung von Node.js v8 oder höher verfügt über eine neue Funktion, die die normale Funktion in eine asynchrone Funktion umwandelt.

util.promisify

Es ist eine großartige Funktion. Hier ist das Beispiel für das Parsen von 10000 Zahlen aus der txt-Datei in ein Array, wobei Inversionen mithilfe der Zusammenführungssortierung für die Zahlen gezählt werden.

// read from txt file
const util = require('util');
const fs = require('fs')
fs.readFileAsync = util.promisify(fs.readFile);
let result = []

const parseTxt = async (csvFile) => {
  let fields, obj
  const data = await fs.readFileAsync(csvFile)
  const str = data.toString()
  const lines = str.split('\r\n')
  // const lines = str
  console.log("lines", lines)
  // console.log("str", str)

  lines.map(line => {
    if(!line) {return null}
    result.push(Number(line))
  })
  console.log("result",result)
  return result
}
parseTxt('./count-inversion.txt').then(() => {
  console.log(mergeSort({arr: result, count: 0}))
})
Seunghun Sunmoon Lee
quelle
1

Um eine große Datei in ein Array einzulesen, können Sie Zeile für Zeile oder Block für Block lesen.

Zeile für Zeile beziehen Sie sich auf meine Antwort hier

var fs = require('fs'),
    es = require('event-stream'),

var lines = [];

var s = fs.createReadStream('filepath')
    .pipe(es.split())
    .pipe(es.mapSync(function(line) {
        //pause the readstream
        s.pause();
        lines.push(line);
        s.resume();
    })
    .on('error', function(err) {
        console.log('Error:', err);
    })
    .on('end', function() {
        console.log('Finish reading.');
        console.log(lines);
    })
);

Stück für Stück finden Sie in diesem Artikel

var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
    offset += bytesRead;
    var str = chunkBuffer.slice(0, bytesRead).toString();
    var arr = str.split('\n');

    if(bytesRead = chunkSize) {
        // the last item of the arr may be not a full line, leave it to the next chunk
        offset -= arr.pop().length;
    }
    lines.push(arr);
}
console.log(lines);
Kris Roofe
quelle