Necesito analizar un archivo línea por línea en el siguiente formato con 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

Representa una gráfica. Las dos primeras líneas son el número de aristas y vértices, seguidos de las aristas.

Puedo cumplir la tarea con algo como:

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

Entiendo que este tipo de cosas pueden no ser para lo que se pensó Node.js, pero el if en cascada en la devolución de llamada line no me parece realmente elegante / legible.

¿Hay alguna manera de leer líneas sincrónicamente de una secuencia como en cualquier otro lenguaje de programación?

Estoy abierto a usar complementos si no hay una solución integrada.

[EDITAR]

Lo sentimos, debería haber dejado más claro que me gustaría evitar cargar el archivo completo en la memoria de antemano

29
Andrea Casaccia 11 dic. 2015 a las 14:59

4 respuestas

La mejor respuesta

Este proyecto en github.com hace exactamente lo que necesitaba:

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(' '));
}
11
Andrea Casaccia 16 mar. 2016 a las 05:19

Mi parte de código habitual para tareas tan simples:

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

lines es una matriz de cadenas sin las vacías.

47
Alexey Ten 11 dic. 2015 a las 12:08

¿Por qué no leerlos todos en una matriz y luego sacar los dos primeros elementos con empalme? Supongo que su ejemplo está mucho más simplificado o simplemente leería todo el archivo en la memoria y lo dividiría. Si su caso real almacena varios gráficos y desea hacer algo cuando cada uno se carga, por ejemplo, puede poner una prueba en su evento de línea

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('close', 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);
}
2
toriningen 16 ene. 2019 a las 01:48

Personalmente, me gusta usar event-stream para hacer frente a las transmisiones. No es necesario aquí, pero lo usé para el ejemplo de código. Es simple, analizo int y pongo todo dentro de edges, luego, cuando la lectura del archivo está hecha, tomo el primer elemento que es vertexes_number, el nuevo primer elemento es 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));
    }));
1
Shanoor 11 dic. 2015 a las 16:21
34223065