Прочитайте строки синхронно из файла в Node.js

Мне нужно проанализировать файл по строкам в следующем формате с помощью 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

Он представляет собой график. Первые две строки - это количество ребер и вершин, за которыми следуют ребра.

Я могу выполнить задачу с чем-то вроде:

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

Я понимаю, что такие вещи могут быть не такими, о чем думали Node.js, но каскадный if в обратном вызове line на самом деле не выглядит элегантно/удобочитаемым.

Есть ли способ читать синхронные строки из потока, как на любом другом языке программирования?

Я открыт для использования плагинов, если нет встроенного решения.

[EDIT]

Извините, я должен был уточнить, что я хотел бы избежать загрузки всего файла в память заранее

Ответ 1

Этот проект на github.com делает именно то, что мне нужно:

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

Ответ 2

Моя обычная часть кода для таких простых задач:

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

lines - это массив строк без пустых.

Ответ 3

Почему бы не прочитать их все в массив, а затем вынуть первые два элемента со сращиванием. Я предполагаю, что ваш пример значительно упрощен, иначе вы просто прочитали бы весь файл в память и разбили его. Если ваш реальный случай хранит несколько графиков, и вы хотите что-то сделать, например, при загрузке каждого из них, вы можете поместить тест в событие строки

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

Ответ 4

Лично мне нравится использовать event-stream для работы с потоками. Здесь не нужно, но я использовал его для образца кода. Это просто, я анализирую int и помещаю все внутри edges, затем, когда чтение файла выполняется, я беру первый элемент, который является vertexes_number, новый первый элемент 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));
    }));