2015-12-11 10 views
7

Ich brauche eine Datei Zeile für Zeile in folgendem Format mit Node.js zu analysieren:lesen Linien synchron aus der Datei in Node.js

13 
13 
0 5 
4 3 
0 1 
9 12 
6 4 
5 4 
0 2 
11 12 
9 10 
0 6 
7 8 
9 11 
5 3 

eine Grafik darstellt. Die ersten beiden Zeilen sind die Anzahl der Kanten und Scheitelpunkte, gefolgt von den Kanten.

ich die Aufgabe mit so etwas wie erreichen kann:

var fs = require('fs'); 
var readline = require('readline'); 
var read_stream = fs.createReadStream(filename); 
var rl = readline.createInterface({ 
    input: read_stream 
}); 
var c = 0; 
var vertexes_number; 
var edges_number; 
var edges = []; 
rl.on('line', function(line){ 
    if (c==0) { 
     vertexes_number = parseInt(line); 
    } else if (c==1) { 
     edges_number = parseInt(line); 
    } else { 
     edges.push(line.split(' ')); 
    } 
    c++; 
}) 
.on('end', function(){ 
    rl.close(); 
}) 

Ich verstehe diese Art von Dingen, vielleicht nicht, was Node.js gedacht wurde, aber die kaskadiert if im line Rückruf nicht wirklich elegant aussehen/lesbar für mich.

Gibt es eine Möglichkeit, synchron Zeilen aus einem Stream wie in jeder anderen Programmiersprache zu lesen?

Ich bin offen Plugins zu verwenden, wenn es keine integrierte Lösung gibt.

[EDIT]

Sorry, ich soll deutlicher herausgestellt habe, dass ich die ganze Datei im Speicher vermeiden möchte Laden vorher

+0

https://nodejs.org/api/fs.html#fs_fs_readfilesync_file_options –

+0

ja, es lädt mit 'fs.readFileSync' und analysieren sie dann mit Ihrem Code synchron nach der Trennung von Zeilenumbrüchen, dh' Linien = fs.readFileSync ('graph.txt'). split (/ [\ n \ r] /); ' –

Antwort

6

Dieses Projekt auf github.com tut genau das, was ich brauchte:

https://github.com/nacholibre/node-readlines

var readlines = require('n-readlines'); 
var liner = new readlines(filename); 

var vertexes_number = parseInt(liner.next().toString('ascii')); 
var edges_number = parseInt(liner.next().toString('ascii')); 
var edges = []; 
var next; 
while (next = liner.next()) { 
    edges.push(next.toString('ascii').split(' ')); 
} 
+0

Gibt es einen guten Weg, dies mit Stdin zu tun? Ich benutze eine Website für die Code-Einreichung und kann/dev/stdin auch nicht als Datei lesen. –

+0

Sie hätten bessere Chancen, eine Antwort zu erhalten, wenn Sie eine neue Frage mit mehr Details posten, anstatt hier zu kommentieren. –

+0

Bereits getan http://stackoverflow.com/questions/43638105/how-to-get-synchronous-readline-or-simulate-it-using-async-in-nodejs –

11

Mein üblicher Codeteil für ein solche einfache Aufgaben:

var lines = require('fs').readFileSync(filename, 'utf-8') 
    .split('\n') 
    .filter(Boolean); 

lines ist ein Array von Strings ohne leere.

+5

Danke. Ich bin jedoch immer noch an Lean-Lösungen interessiert, die nicht die gesamte Datei in den Speicher laden. –

+2

Auf diese Weise kann keine große Datei verarbeitet werden, z. B. Protokolldatei mit 10000000 Zeilen, da große Dateien mit Puffer in den Speicher geladen werden können, die toString-Methode von nodejs jedoch nicht mit zu großen Pufferobjekten umgehen kann. – zhuyingda

0

Persönlich verwende ich gerne event-stream, um mit Streams umzugehen. Es ist hier nicht notwendig, aber ich habe es für das Codebeispiel verwendet. Es ist ganz einfach, ich analysiere alles in edges in int und setze, dann, wenn die Datei Lesung geschehen ist, nehme ich das erste Element weicht vertexes_number ist, das neue erste Element ist edges_number

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

var filename = 'parse-file.txt'; 

var vertexes_number, edges_number; 
var edges = []; 

fs.createReadStream(filename) 
    .pipe(es.split()) // split by lines 
    .pipe(es.map(function (line, next) { 
     // split and convert all to numbers 
     edges.push(line.split(' ').map((n) => +n)); 

     next(null, line); 
    })).pipe(es.wait(function (err, body) { 
     // the first element is an array containing vertexes_number 
     vertexes_number = edges.shift().pop(); 

     // the following element is an array containing edges_number 
     edges_number = edges.shift().pop(); 

     console.log('done'); 
     console.log('vertexes_number: ' + vertexes_number); 
     console.log('edges_number: ' + edges_number); 
     console.log('edges: ' + JSON.stringify(edges, null, 3)); 
    })); 
+0

Dies ist die beste Methode bisher, wenn Sie nicht die gesamte Datei in den Speicher laden müssen. –

+0

Ja, es ist sehr gut diese Methode, aber immer noch async, die Fragen gibt Sync-Modus, aber nicht nützlich für diesen Fall. –

1

Warum liest sie nicht alle in eine Array und dann die ersten beiden Elemente mit Spleiß herausnehmen. Ich nehme an, dass Ihr Beispiel sehr vereinfacht ist, oder Sie würden einfach die ganze Datei in den Speicher einlesen und aufteilen. Wenn Ihr tatsächlicher Fall mehr Grafiken speichert und Sie wollen etwas tun, wenn jeder zum Beispiel geladen wird, können Sie einen Test in Ihrer Linie Veranstaltung setzen

var fs = require('fs'); 
var readline = require('readline'); 
var read_stream = fs.createReadStream(filename); 
var rl = readline.createInterface({ 
    input: read_stream 
}); 

var buffer = []; 

rl.on('line', function(line){ 
    buffer.push(line.split(' ')); 
    //Not sure what your actual requirement is but if you want to do 
    //something like display a graph once one has loaded 
    //obviously need to be able to determine when one has completed loading 
    if (buffer.length == GRAPHLENGTH) { //or some other test 
     displayGraph(buffer); 
     buffer = []; 
    }  
}) 
.on('end', function(){ 
    //or do it here if there is only one graph 
    //displayGraph(buffer); 
    rl.close(); 
}) 

function displayGraph(buffer){ 
    var vertexes_number = parseInt(buffer.splice(0,1)); 
    var edges_number = parseInt(buffer.splice(0,1)); 
    var edges = buffer; 

    //doYourThing(vertexes_number, edges_number, edges); 
} 
+1

Es sollte erwähnt werden, dass es im Falle eines Dateisystems kein 'Ende'-Ereignis gibt, sondern' Schließen 'am Ende der Datei ausgegeben wird. –