node.js: lee un archivo de texto en una matriz. (Cada línea es un elemento de la matriz).

163

Me gustaría leer un archivo muy, muy grande en una matriz de JavaScript en node.js.

Entonces, si el archivo es así:

first line
two 
three
...
...

Tendría la matriz:

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

La función se vería así:

var array = load(filename); 

Por lo tanto, la idea de cargarlo todo como una cadena y luego dividirlo no es aceptable.

chacko
fuente
Esta pregunta necesita una edición y limpieza serias. Dice leer un archivo de texto en una matriz , pero cuando lee todas las respuestas y comentarios, realmente significa leer un archivo de texto de una línea a la vez . Para esa pregunta, @zswang tiene la mejor respuesta hasta ahora.
Jess
solo lea ese archivo e inserte cada línea en una matriz: stackoverflow.com/a/34033928/1536309
Blair Anderson

Respuestas:

89

Si puede ajustar los datos finales en una matriz, ¿no podría también ajustarlos en una cadena y dividirlos, como se ha sugerido? En cualquier caso, si desea procesar el archivo una línea a la vez, también puede intentar algo como esto:

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

EDITAR: (en respuesta al comentario de phopkins ) Creo que (al menos en versiones más recientes) la subcadena no copia datos, sino que crea un objeto SlicedString especial (de un vistazo rápido al código fuente v8). En cualquier caso, aquí hay una modificación que evita la subcadena mencionada (probada en un archivo por varios megabytes de "Todo el trabajo y nada de juego convierte a Jack en un niño aburrido"):

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
fuente
Gracias. para responder a su pregunta: no, la cadena sería demasiado grande.
chacko
77
Intenté esto en archivos de alrededor de 2 MB más o menos y fue dolorosamente lento, mucho más lento que leer los archivos sincrónicamente en una cadena. Creo que el problema es la línea restante = resto.substring. Los "datos" de Node pueden proporcionarle mucho a la vez, y hacer esa copia para cada línea se convierte rápidamente en O (n ^ 2).
Fiona Hopkins
La respuesta de @ Finbar es mucho mejor
rü-
442

Sincrónico:

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

Asincrónico:

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
fuente
11
Gracias. Lamentablemente tuve que editar mi pregunta. Me refiero a cómo leer un archivo enormemente grande. Leerlo todo en una cadena no es aceptable.
chacko
1
Justo lo que necesitaba. Simple y rapido.
Hcabnettek
16
Encontré que hacer esto en un archivo creado por Windows, tuve que dividir \ r \ n pero eso rompió las Mac; entonces un más robusto; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); trabajó para ambos
Will Hancock
66
+1 Hay algún problema en Stackoverflow. Ahora, a menudo encuentro respuestas muy votadas después de desplazarme demasiado. Este también es un ejemplo de esto. Tiene la votación más alta, pero está en la parte inferior de la página, por último. Creo que Stackoverflow necesita mejorar su algoritmo de pedido.
shashwat
1
@shashwat La persona que hace la pregunta decide cuál es la respuesta correcta. En este caso, necesitaban una solución de transmisión para archivos grandes y poner el archivo completo en una cadena es inaceptable. No hay nada malo con SO, realmente.
legalizar el
73

Utilizando el Node.js módulo readline .

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
fuente
1
Lamentablemente, hay un problema con esta solución: no se obtiene la última línea si el archivo no tiene una \nal final. Ver: stackoverflow.com/questions/18450197/…
Yves M.
8
Node ha solucionado ese problema con el \ 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
fuente
1
Para evitar que se lance lo anterior TypeError: fs.readFileSync(...).split is not a function, debe usar .toString () así:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285
11

use readline ( documentación ). Aquí hay un ejemplo leyendo un archivo CSS, analizando íconos y escribiéndolos en json

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
fuente
6

file.linescon paquete JFile

Seudo

var JFile=require('jfile');

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

No olvides antes:

npm install jfile --save
Abdennour TOUMI
fuente
5

Con un BufferedReader , pero la función debe ser asíncrona:

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
fuente
4

Solo quiero agregar una excelente respuesta a @finbarr, una pequeña solución en el ejemplo asincrónico:

Asincrónico:

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 () es lo que libera el asíncrono. llamada.

HernanFila
fuente
3

Esta es una variación de la respuesta anterior de @mtomis.

Crea una corriente de líneas. Emite eventos de 'datos' y 'fin', lo que le permite manejar el final de la transmisión.

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;

Úselo como envoltorio:

var lineInput = new LineStream(input);

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

lineInput.on('end', function() {
    // wrap it up
});
oferei
fuente
1
Terminará teniendo eventos compartidos entre instancias. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP
¿De qué instancias estás hablando exactamente?
oferei
1
intente crear y var li1 = new LineStream(input1), li2 = new LineStream(input2);luego cuente cuántas veces se dispara 'final' para cada uno
CTAPbIu_MABP
lo intenté. 'end' se disparó una vez para cada instancia. 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'); }); salida: cada línea en el archivo de texto se disparó una vez para cada instancia. así fue 'fin'.
oferei
2

Tuve el mismo problema y lo resolví con el módulo línea por línea

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

Al menos para mí funciona de maravilla, tanto en modo síncrono como asíncrono.

Además, el problema con las líneas que terminan sin terminar \ n se puede resolver con la opción:

{ encoding: 'utf8', skipEmptyLines: false }

Procesamiento síncrono de líneas:

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
fuente
2

Usar Node.js v8 o posterior tiene una nueva característica que convierte la función normal en una función asíncrona.

util.promisify

Es una característica asombrosa. Este es el ejemplo de analizar 10000 números del archivo txt en una matriz, contando las inversiones mediante la combinación de clasificación en los números.

// 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
fuente
1

Para leer un archivo grande en una matriz, puede leer línea por línea o fragmento por fragmento.

línea por línea consulte mi respuesta aquí

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

trozo a trozo consulte este artículo

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
fuente