¿Leer un archivo línea por línea en node.js?

553

Estoy tratando de leer un archivo grande una línea a la vez. Encontré una pregunta sobre Quora que trataba el tema, pero me faltan algunas conexiones para que todo encaje.

 var Lazy=require("lazy");
 new Lazy(process.stdin)
     .lines
     .forEach(
          function(line) { 
              console.log(line.toString()); 
          }
 );
 process.stdin.resume();

Lo que me gustaría averiguar es cómo podría leer una línea a la vez desde un archivo en lugar de STDIN como en este ejemplo.

Lo intenté:

 fs.open('./VeryBigFile.csv', 'r', '0666', Process);

 function Process(err, fd) {
    if (err) throw err;
    // DO lazy read 
 }

Pero no está funcionando. Sé que en un apuro podría recurrir a usar algo como PHP, pero me gustaría resolver esto.

No creo que la otra respuesta funcione ya que el archivo es mucho más grande que el servidor en el que lo estoy ejecutando tiene memoria.

Alex C
fuente
2
Esto resulta ser bastante difícil usando solo bajo nivel fs.readSync(). Puede leer octetos binarios en un búfer, pero no hay una manera fácil de tratar con caracteres UTF-8 o UTF-16 parciales sin inspeccionar el búfer antes de traducirlo a cadenas de JavaScript y buscar EOL. El Buffer()tipo no tiene un conjunto de funciones tan rico para operar en sus instancias como las cadenas nativas, pero las cadenas nativas no pueden contener datos binarios. Me parece que la falta de una forma integrada de leer líneas de texto de archivos arbitrarios es una brecha real en node.js.
hippietrail
55
Las líneas vacías leídas por este método se convierten en una línea con un solo 0 (código de carácter real para 0) en ellas. Tuve que hackear esta línea allí:if (line.length==1 && line[0] == 48) special(line);
Thabo
2
También se podría usar el paquete 'línea por línea' que hace el trabajo perfectamente.
Patrice
1
Actualice la pregunta para decir que la solución es usar una secuencia de transformación
Gabriel Llamas
2
@DanDascalescu si lo desea, puede agregar esto a la lista: su ejemplo aterrizó ligeramente modificado en nodelos documentos API de github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe el

Respuestas:

790

Desde Node.js v0.12 y como de Node.js v4.0.0, hay una estable readline módulo de núcleo. Aquí está la forma más fácil de leer líneas de un archivo, sin ningún módulo externo:

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

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

O alternativamente:

var lineReader = require('readline').createInterface({
  input: require('fs').createReadStream('file.in')
});

lineReader.on('line', function (line) {
  console.log('Line from file:', line);
});

La última línea se lee correctamente (a partir del Nodo v0.12 o posterior), incluso si no hay una final \n.

ACTUALIZACIÓN : este ejemplo se ha agregado a la documentación oficial de la API de Node .

Dan Dascalescu
fuente
77
necesita un terminal: falso en la definición de
createInterface
64
¿Cómo determinar la última línea? Al ver un evento de "cierre":rl.on('close', cb)
Verde
27
Readline es para un propósito similar a GNU Readline , no para leer archivos línea por línea. Hay varias advertencias al usarlo para leer archivos y esta no es una práctica recomendada.
Desnudo
8
@Nakedible: interesante. ¿Podría publicar una respuesta con un método mejor?
Dan Dascalescu
66
Considero que github.com/jahewson/node-byline es la mejor implementación de la lectura línea por línea, pero las opiniones pueden variar.
Desnudo
165

Para una operación tan simple, no debería haber ninguna dependencia en módulos de terceros. Con calma.

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

var rd = readline.createInterface({
    input: fs.createReadStream('/path/to/file'),
    output: process.stdout,
    console: false
});

rd.on('line', function(line) {
    console.log(line);
});
kofrasa
fuente
33
Lamentablemente, esta atractiva solución no funciona correctamente: los lineeventos se \nproducen solo después de golpear , es decir, se pierden todas las alternativas (ver unicode.org/reports/tr18/#Line_Boundaries ). # 2, los datos después del último \nse ignoran silenciosamente (consulte stackoverflow.com/questions/18450197/… ). Llamaría a esta solución peligrosa porque funciona para el 99% de todos los archivos y para el 99% de los datos, pero falla silenciosamente para el resto. cada vez que lo hace fs.writeFileSync( path, lines.join('\n')), ha escrito un archivo que solo será leído parcialmente por la solución anterior.
flujo
44
Hay un problema con esta solución. Si usa your.js <lines.txt no obtendrá la última línea. Si no tiene un '\ n' al final del curso.
zag2art
El readlinepaquete se comporta de maneras realmente extrañas para un programador experimentado de Unix / Linux.
Puntiagudo
11
rd.on("close", ..);puede usarse como una devolución de llamada (ocurre cuando se leen todas las líneas)
Luca Steeb
66
El problema de "datos después del último \ n" parece estar resuelto en mi versión del nodo (0.12.7). Así que prefiero esta respuesta, que parece la más simple y elegante.
Myk Melez
63

No tiene que openusar el archivo, sino que debe crear un ReadStream.

fs.createReadStream

Luego pasa esa corriente a Lazy

Raynos
fuente
2
¿Hay algo así como un evento final para Lazy? ¿Cuándo se han leído todas las líneas?
Max
1
@Max, prueba:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi
66
@Cecchi y @Max, no use join porque almacenará todo el archivo en la memoria intermedia. En cambio, solo escuche el evento 'final':new lazy(...).lines.forEach(...).on('end', function() {...})
Corin
3
@Cecchi, @Corin y @Max: por lo que vale, me volví loco encadenado .on('end'... después .forEach(...) , cuando de hecho todo se comportó como se esperaba cuando limité el evento primero .
crowjonah
52
Este resultado es muy alto en los resultados de búsqueda, por lo que vale la pena señalar que Lazy parece abandonada. Han pasado 7 meses sin cambios, y tiene algunos errores horribles (última línea ignorada, pérdidas masivas de memoria, etc.).
blu
38

hay un módulo muy bueno para leer un archivo línea por línea, se llama lector de línea

con él simplemente escribes:

var lineReader = require('line-reader');

lineReader.eachLine('file.txt', function(line, last) {
  console.log(line);
  // do whatever you want with line...
  if(last){
    // or check if it's the last one
  }
});

incluso puede iterar el archivo con una interfaz "estilo java", si necesita más control:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});
Polaretto
fuente
44
Esto funciona bien Incluso lee la última línea (!). Vale la pena mencionar que mantiene el \ r si es un archivo de texto de estilo Windows. line.trim () hace el truco de eliminar el \ r adicional.
Pierre-Luc Bertrand
Es subóptimo en que la entrada solo puede ser de un archivo con nombre, y no (para un ejemplo obvio y extremadamente importante process/stdin). Al menos, si puede, ciertamente no es obvio al leer el código y al intentarlo.
Puntiagudo
2
Mientras tanto, hay una forma integrada de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
Esto es viejo, pero en caso de que alguien se tope con él: function(reader)y function(line)debería ser: function(err,reader)y function(err,line).
jallmer
1
Solo para el registro, line-readerlee el archivo de forma asincrónica. La alternativa sincrónica esline-reader-sync
Prajwal Dhatwalia
31
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})
John Williams
fuente
42
Esto leerá todo el archivo en la memoria, luego lo dividirá en líneas. No es lo que hacen las preguntas. El punto es poder leer archivos grandes secuencialmente, bajo demanda.
Dan Dascalescu
2
Esto se ajusta a mi caso de uso, estaba buscando una forma sencilla de convertir la entrada de un script a otro formato. ¡Gracias!
Callat
23

Actualización en 2019

Un ejemplo impresionante ya está publicado en la documentación oficial de Nodejs. aquí

Esto requiere que el último Nodejs esté instalado en su máquina. > 11.4

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

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();
Desarrollador principal
fuente
Esta respuesta es mucho mejor que cualquiera de las anteriores gracias a su comportamiento basado en promesas, que indica claramente el EOF.
phil294
Gracias, eso es dulce.
Goran Stoyanov
3
Tal vez esto sea obvio para otros, pero me llevó un tiempo depurarlo: si tiene alguna awaits entre la createInterface()llamada y el inicio del for awaitbucle, perderá misteriosamente las líneas desde el inicio del archivo. createInterface()comienza inmediatamente a emitir líneas detrás de escena, y el iterador asíncrono creado implícitamente con const line of rlno puede comenzar a escuchar esas líneas hasta que se crea.
andrewdotn
19

Viejo tema, pero esto funciona:

var rl = readline.createInterface({
      input : fs.createReadStream('/path/file.txt'),
      output: process.stdout,
      terminal: false
})
rl.on('line',function(line){
     console.log(line) //or parse line
})

Simple. No es necesario un módulo externo.

nf071590
fuente
2
Si obtiene readline is not definedo fs is not defined, agregue var readline = require('readline');y var fs = require('fs');para que esto funcione. De lo contrario dulce, dulce código. Gracias.
bergie3000
12
Esta respuesta es una copia exacta de una respuesta anterior , pero sin los comentarios que advierten que el paquete readline está marcado como inestable (todavía inestable a partir de abril de 2015) y, a mediados de 2013, tuvo problemas para leer las últimas líneas de un archivo sin final de línea . El último problema de línea surgió la primera vez que lo usé en v0.10.35, y luego desapareció. / argh
ruffin
No necesita especificar la salida si todo lo que hace es leer desde una secuencia de archivos .
Dan Dascalescu
18

Siempre puede rodar su propio lector de línea. Todavía no he comparado este fragmento, pero divide correctamente la secuencia entrante de fragmentos en líneas sin el '\ n' final

var last = "";

process.stdin.on('data', function(chunk) {
    var lines, i;

    lines = (last+chunk).split("\n");
    for(i = 0; i < lines.length - 1; i++) {
        console.log("line: " + lines[i]);
    }
    last = lines[i];
});

process.stdin.on('end', function() {
    console.log("line: " + last);
});

process.stdin.resume();

Se me ocurrió esto cuando trabajé en un script de análisis de registro rápido que necesitaba acumular datos durante el análisis de registro y sentí que sería bueno intentar hacerlo usando js y node en lugar de usar perl o bash.

De todos modos, creo que los scripts pequeños de nodejs deberían ser independientes y no depender de módulos de terceros, por lo que después de leer todas las respuestas a esta pregunta, cada uno con varios módulos para manejar el análisis de líneas, una solución de 13 nodejs nativos de SLOC podría ser de interés.

Ernelli
fuente
No parece haber una manera trivial de extender esto para trabajar con archivos arbitrarios además de stdin... a menos que me falte algo.
hippietrail
3
@hippietrail puede crear una ReadStreamcon fs.createReadStream('./myBigFile.csv')y usarlo en lugar destdin
nolith
2
¿Se garantiza que cada fragmento contenga solo líneas completas? ¿Se garantiza que los caracteres UTF-8 de varios bytes no se dividan en los límites de los fragmentos?
hippietrail
1
@hippietrail No creo que esta implementación maneje correctamente los caracteres multibyte. Para eso, primero se deben convertir correctamente los búferes en cadenas y realizar un seguimiento de los caracteres que se dividen entre dos búferes. Para hacerlo correctamente, se puede usar el StringDecoder incorporado
Ernelli
Mientras tanto, hay una forma integrada de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
12

Con el módulo portador :

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});
Touv
fuente
Agradable. Esto también funciona para cualquier archivo de entrada: var inStream = fs.createReadStream('input.txt', {flags:'r'}); pero su sintaxis es más limpia que el método documentado de usar .on ():carrier.carry(inStream).on('line', function(line) { ...
Brent Faust
parece que el operador solo maneja \r\ny \ntermina las líneas. Si alguna vez necesita lidiar con archivos de prueba de estilo MacOS anteriores a OS X, se utilizan \ry el operador no maneja esto. Sorprendentemente, todavía hay tales archivos flotando en la naturaleza. Es posible que también deba manejar explícitamente la lista de materiales Unicode (marca de orden de bytes), esto se usa al comienzo de los archivos de texto en la esfera de influencia de MS Windows.
hippietrail
Mientras tanto, hay una forma integrada de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
9

Terminé con una pérdida masiva de memoria masiva usando Lazy para leer línea por línea al intentar procesar esas líneas y escribirlas en otra secuencia debido a la forma en que funciona el drenaje / pausa / reanudación en el nodo (ver: http: // elegantcode .com / 2011/04/06 / tomando-baby-steps-with-node-js-pumping-data-between-streams ((me encanta este chico por cierto)). No he mirado lo suficientemente de cerca a Lazy para entender exactamente por qué, pero no pude detener mi flujo de lectura para permitir un drenaje sin que Lazy salga.

Escribí el código para procesar archivos csv masivos en documentos xml, puedes ver el código aquí: https://github.com/j03m/node-csv2xml

Si ejecuta las revisiones anteriores con la línea Lazy, se filtra. La última revisión no pierde nada y probablemente pueda usarla como base para un lector / procesador. Aunque tengo algunas cosas personalizadas allí.

Editar: Supongo que también debería tener en cuenta que mi código con Lazy funcionó bien hasta que me encontré escribiendo fragmentos xml lo suficientemente grandes que drenan / pausan / reanudan por necesidad. Para trozos más pequeños estaba bien.

j03m
fuente
Mientras tanto, hay una manera mucho más simple de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
Sip. Esa es la forma correcta ahora. Pero esto fue a partir de 2011. :)
j03m
8

Editar:

Utiliza una secuencia de transformación .


Con un BufferedReader puedes leer líneas.

new BufferedReader ("lorem ipsum", { encoding: "utf8" })
    .on ("error", function (error){
        console.log ("error: " + error);
    })
    .on ("line", function (line){
        console.log ("line: " + line);
    })
    .on ("end", function (){
        console.log ("EOF");
    })
    .read ();
Gabriel Llamas
fuente
1
Mientras tanto, hay una manera mucho más simple de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
7

Desde que publiqué mi respuesta original, descubrí que dividir es un módulo de nodo muy fácil de usar para leer líneas en un archivo; Que también acepta parámetros opcionales.

var split = require('split');
fs.createReadStream(file)
    .pipe(split())
    .on('data', function (line) {
      //each chunk now is a seperate line! 
    });

No he probado en archivos muy grandes. Háganos saber si lo hace.

nf071590
fuente
6

Estaba frustrado por la falta de una solución integral para esto, así que armé mi propio intento ( git / npm ). Lista de características copiadas y pegadas:

  • Procesamiento de línea interactivo (basado en devolución de llamada, sin cargar todo el archivo en la RAM)
  • Opcionalmente, devuelva todas las líneas en una matriz (modo detallado o sin formato)
  • Interrumpa interactivamente la transmisión o realice un mapa / filtro como el procesamiento
  • Detectar cualquier convención de nueva línea (PC / Mac / Linux)
  • Eof correcto / tratamiento de última línea
  • Manejo correcto de caracteres UTF-8 de varios bytes
  • Recupere la información de desplazamiento de byte y longitud de byte por línea
  • Acceso aleatorio, utilizando compensaciones basadas en líneas o en bytes
  • Asigna automáticamente información de desplazamiento de línea para acelerar el acceso aleatorio
  • Cero dependencias
  • Pruebas

¿NIH? Tú decides :-)

panta82
fuente
5
function createLineReader(fileName){
    var EM = require("events").EventEmitter
    var ev = new EM()
    var stream = require("fs").createReadStream(fileName)
    var remainder = null;
    stream.on("data",function(data){
        if(remainder != null){//append newly received data chunk
            var tmp = new Buffer(remainder.length+data.length)
            remainder.copy(tmp)
            data.copy(tmp,remainder.length)
            data = tmp;
        }
        var start = 0;
        for(var i=0; i<data.length; i++){
            if(data[i] == 10){ //\n new line
                var line = data.slice(start,i)
                ev.emit("line", line)
                start = i+1;
            }
        }
        if(start<data.length){
            remainder = data.slice(start);
        }else{
            remainder = null;
        }
    })

    stream.on("end",function(){
        if(null!=remainder) ev.emit("line",remainder)
    })

    return ev
}


//---------main---------------
fileName = process.argv[2]

lineReader = createLineReader(fileName)
lineReader.on("line",function(line){
    console.log(line.toString())
    //console.log("++++++++++++++++++++")
})
user531097
fuente
Probaré esto, pero ¿puedes decirme si está garantizado que nunca se rompan los caracteres multibyte? (UTF-8 / UTF-16)
hippietrail
2
@hippietrail: La respuesta es no para UTF-8, a pesar de que está trabajando en una secuencia de bytes en lugar de una secuencia de caracteres. Se rompe en las nuevas líneas (0x0a). En UTF-8, todos los bytes de un carácter multibyte tienen su conjunto de bits de orden superior. Por lo tanto, ningún carácter multibyte puede incluir una nueva línea incrustada u otro carácter ASCII común. Sin embargo, UTF-16 y UTF-32 son otro asunto.
George
@George: Creo que nos entendemos mal. Como CR y LF están dentro del rango ASCII y UTF-8 conserva los 128 caracteres ASCII sin cambios, ni CR ni LF pueden formar parte de un carácter UTF-8 multibyte. Lo que estaba preguntando es si la datallamada stream.on("data")podría comenzar o terminar con solo una parte de un carácter UTF-8 multibyte como el que está U+10D0formado por los tres bytese1 83 90
hippietrail
1
Esto todavía carga todo el contenido del archivo en la memoria antes de convertirlo en una "nueva línea". Esto no LEE una línea a la vez, sino que toma TODAS las líneas y luego las divide según la longitud del búfer de "nueva línea". Este método anula el propósito de crear una secuencia.
Justin
Mientras tanto, hay una manera mucho más simple de leer líneas de un archivo, utilizando el readlinemódulo central .
Dan Dascalescu
5

Quería abordar este mismo problema, básicamente lo que en Perl sería:

while (<>) {
    process_line($_);
}

Mi caso de uso era solo una secuencia de comandos independiente, no un servidor, por lo que sincronizar estaba bien. Estos fueron mis criterios:

  • El código síncrono mínimo que podría reutilizarse en muchos proyectos.
  • No hay límites en el tamaño del archivo o el número de líneas.
  • No hay límites en la longitud de las líneas.
  • Capaz de manejar Unicode completo en UTF-8, incluidos los caracteres más allá del BMP.
  • Capaz de manejar las terminaciones de línea * nix y Windows (la Mac antigua no me era necesaria).
  • Los caracteres de finalización de línea se incluirán en las líneas.
  • Capaz de manejar la última línea con o sin caracteres de fin de línea.
  • No use ninguna biblioteca externa que no esté incluida en la distribución node.js.

Este es un proyecto para mí para tener una idea del código de tipo scripting de bajo nivel en node.js y decidir qué tan viable es como reemplazo de otros lenguajes de scripting como Perl.

Después de un sorprendente esfuerzo y un par de falsos comienzos, este es el código que se me ocurrió. Es bastante rápido pero menos trivial de lo que esperaba: (bifurca en GitHub)

var fs            = require('fs'),
    StringDecoder = require('string_decoder').StringDecoder,
    util          = require('util');

function lineByLine(fd) {
  var blob = '';
  var blobStart = 0;
  var blobEnd = 0;

  var decoder = new StringDecoder('utf8');

  var CHUNK_SIZE = 16384;
  var chunk = new Buffer(CHUNK_SIZE);

  var eolPos = -1;
  var lastChunk = false;

  var moreLines = true;
  var readMore = true;

  // each line
  while (moreLines) {

    readMore = true;
    // append more chunks from the file onto the end of our blob of text until we have an EOL or EOF
    while (readMore) {

      // do we have a whole line? (with LF)
      eolPos = blob.indexOf('\n', blobStart);

      if (eolPos !== -1) {
        blobEnd = eolPos;
        readMore = false;

      // do we have the last line? (no LF)
      } else if (lastChunk) {
        blobEnd = blob.length;
        readMore = false;

      // otherwise read more
      } else {
        var bytesRead = fs.readSync(fd, chunk, 0, CHUNK_SIZE, null);

        lastChunk = bytesRead !== CHUNK_SIZE;

        blob += decoder.write(chunk.slice(0, bytesRead));
      }
    }

    if (blobStart < blob.length) {
      processLine(blob.substring(blobStart, blobEnd + 1));

      blobStart = blobEnd + 1;

      if (blobStart >= CHUNK_SIZE) {
        // blobStart is in characters, CHUNK_SIZE is in octets
        var freeable = blobStart / CHUNK_SIZE;

        // keep blob from growing indefinitely, not as deterministic as I'd like
        blob = blob.substring(CHUNK_SIZE);
        blobStart -= CHUNK_SIZE;
        blobEnd -= CHUNK_SIZE;
      }
    } else {
      moreLines = false;
    }
  }
}

Probablemente podría limpiarse más, fue el resultado de prueba y error.

hippietrail
fuente
5

En la mayoría de los casos, esto debería ser suficiente:

const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, file) => {
  const lines = file.split('\n')

  for (let line of lines)
    console.log(line)
});
dorio
fuente
2

Lector de línea basado en generador: https://github.com/neurosnap/gen-readlines

var fs = require('fs');
var readlines = require('gen-readlines');

fs.open('./file.txt', 'r', function(err, fd) {
  if (err) throw err;
  fs.fstat(fd, function(err, stats) {
    if (err) throw err;

    for (var line of readlines(fd, stats.size)) {
      console.log(line.toString());
    }

  });
});
neurosnap
fuente
2

Si desea leer un archivo línea por línea y escribir esto en otro:

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

function readFileLineByLine(inputFile, outputFile) {

   var instream = fs.createReadStream(inputFile);
   var outstream = new Stream();
   outstream.readable = true;
   outstream.writable = true;

   var rl = readline.createInterface({
      input: instream,
      output: outstream,
      terminal: false
   });

   rl.on('line', function (line) {
        fs.appendFileSync(outputFile, line + '\n');
   });
};
Thami Bouchnafa
fuente
¿Cuál es la diferencia entre la tuya y la respuesta de kofrasa?
Buffalo
2
var fs = require('fs');

function readfile(name,online,onend,encoding) {
    var bufsize = 1024;
    var buffer = new Buffer(bufsize);
    var bufread = 0;
    var fd = fs.openSync(name,'r');
    var position = 0;
    var eof = false;
    var data = "";
    var lines = 0;

    encoding = encoding || "utf8";

    function readbuf() {
        bufread = fs.readSync(fd,buffer,0,bufsize,position);
        position += bufread;
        eof = bufread ? false : true;
        data += buffer.toString(encoding,0,bufread);
    }

    function getLine() {
        var nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl && eof) return fs.closeSync(fd), online(data,++lines), onend(lines); 
        if (!hasnl && !eof) readbuf(), nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl) return process.nextTick(getLine);
        var line = data.substr(0,nl);
        data = data.substr(nl+1);
        if (data[0] === "\n") data = data.substr(1);
        online(line,++lines);
        process.nextTick(getLine);
    }
    getLine();
}

Tuve el mismo problema y se me ocurrió que la solución anterior parece similar a los demás, pero es aSync y puede leer archivos grandes muy rápidamente

Espera que esto ayude

usuario2056154
fuente
1

Tengo un pequeño módulo que hace esto bien y es utilizado por muchos otros proyectos npm readline Nota que en el nodo v10 hay un módulo de readline nativo, así que volví a publicar mi módulo como linebyline https://www.npmjs.com/package/ linea por linea

Si no desea utilizar el módulo, la función es muy simple:

var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
util = require('util'),
newlines = [
  13, // \r
  10  // \n
];
var readLine = module.exports = function(file, opts) {
if (!(this instanceof readLine)) return new readLine(file);

EventEmitter.call(this);
opts = opts || {};
var self = this,
  line = [],
  lineCount = 0,
  emit = function(line, count) {
    self.emit('line', new Buffer(line).toString(), count);
  };
  this.input = fs.createReadStream(file);
  this.input.on('open', function(fd) {
    self.emit('open', fd);
  })
  .on('data', function(data) {
   for (var i = 0; i < data.length; i++) {
    if (0 <= newlines.indexOf(data[i])) { // Newline char was found.
      lineCount++;
      if (line.length) emit(line, lineCount);
      line = []; // Empty buffer.
     } else {
      line.push(data[i]); // Buffer new line data.
     }
   }
 }).on('error', function(err) {
   self.emit('error', err);
 }).on('end', function() {
  // Emit last line if anything left over since EOF won't trigger it.
  if (line.length){
     lineCount++;
     emit(line, lineCount);
  }
  self.emit('end');
 }).on('close', function() {
   self.emit('close');
 });
};
util.inherits(readLine, EventEmitter);
Maleck13
fuente
1

Otra solución es ejecutar la lógica a través del ejecutor secuencial nsynjs . Lee el archivo línea por línea usando el módulo de línea de lectura del nodo, y no usa promesas o recursividad, por lo tanto, no fallará en archivos grandes. Así es como se verá el código:

var nsynjs = require('nsynjs');
var textFile = require('./wrappers/nodeReadline').textFile; // this file is part of nsynjs

function process(textFile) {

    var fh = new textFile();
    fh.open('path/to/file');
    var s;
    while (typeof(s = fh.readLine(nsynjsCtx).data) != 'undefined')
        console.log(s);
    fh.close();
}

var ctx = nsynjs.run(process,{},textFile,function () {
    console.log('done');
});

El código anterior se basa en este examen: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js

amaksr
fuente
1

Dos preguntas que debemos hacernos al hacer tales operaciones son:

  1. ¿Cuál es la cantidad de memoria utilizada para realizarlo?
  2. ¿El consumo de memoria aumenta drásticamente con el tamaño del archivo?

Soluciones como require('fs').readFileSync()carga todo el archivo en la memoria. Eso significa que la cantidad de memoria requerida para realizar operaciones será casi equivalente al tamaño del archivo. Deberíamos evitarlos para algo más grande que50mbs

Podemos rastrear fácilmente la cantidad de memoria utilizada por una función colocando estas líneas de código después de la invocación de la función:

    const used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(
      `The script uses approximately ${Math.round(used * 100) / 100} MB`
    );

En este momento la mejor manera de leer las líneas particulares de un archivo de gran tamaño está utilizando el nodo de readline . La documentación tiene ejemplos asombrosos .

Aunque no necesitamos ningún módulo de terceros para hacerlo. Pero, si está escribiendo un código de empresa, debe manejar muchos casos extremos. Tuve que escribir un módulo muy liviano llamado Apick File Storage para manejar todos esos casos .

Módulo Apick File Storage: https://www.npmjs.com/package/apickfs Documentación: https://github.com/apickjs/apickFS#readme

Archivo de ejemplo: https://1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx

Ejemplo: instalar módulo

npm i apickfs
// import module
const apickFileStorage = require('apickfs');
//invoke readByLineNumbers() method
apickFileStorage
  .readByLineNumbers(path.join(__dirname), 'big.txt', [163845])
  .then(d => {
    console.log(d);
  })
  .catch(e => {
    console.log(e);
  });

Este método se probó con éxito con archivos densos de hasta 4 GB.

big.text es un archivo de texto denso con 163.845 líneas y es de 124 Mb. La secuencia de comandos para leer 10 líneas diferentes de este archivo utiliza aproximadamente solo 4,63 MB de memoria solamente. Y analiza JSON válido para objetos o matrices de forma gratuita. 🥳 Impresionante !!

Podemos leer una sola línea del archivo o cientos de líneas del archivo con muy poco consumo de memoria.

vivek agarwal
fuente
0

yo uso esto:

function emitLines(stream, re){
    re = re && /\n/;
    var buffer = '';

    stream.on('data', stream_data);
    stream.on('end', stream_end);

    function stream_data(data){
        buffer += data;
        flush();
    }//stream_data

    function stream_end(){
        if(buffer) stream.emmit('line', buffer);
    }//stream_end


    function flush(){
        var re = /\n/;
        var match;
        while(match = re.exec(buffer)){
            var index = match.index + match[0].length;
            stream.emit('line', buffer.substring(0, index));
            buffer = buffer.substring(index);
            re.lastIndex = 0;
        }
    }//flush

}//emitLines

use esta función en una secuencia y escuche los eventos de línea que se emitirán.

gramo-

Elmer
fuente
0

Si bien probablemente debería usar el readlinemódulo como sugiere la respuesta principal, readlineparece estar orientado hacia las interfaces de línea de comando en lugar de la lectura de línea. También es un poco más opaco con respecto al almacenamiento en búfer. (Cualquiera que necesite un lector orientado a la línea de transmisión probablemente querrá ajustar los tamaños del búfer). El módulo de línea de lectura es ~ 1000 líneas, mientras que esto, con estadísticas y pruebas, es 34.

const EventEmitter = require('events').EventEmitter;
class LineReader extends EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.totalChars = 0;
        this.totalLines = 0;
        this.leftover = '';

        f.on('data', (chunk)=>{
            this.totalChars += chunk.length;
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) lines.pop();
            this.totalLines += lines.length;
            for (let l of lines) this.onLine(l);
        });
        // f.on('error', ()=>{});
        f.on('end', ()=>{console.log('chars', this.totalChars, 'lines', this.totalLines)});
    }
    onLine(l){
        this.emit('line', l);
    }
}
//Command line test
const f = require('fs').createReadStream(process.argv[2], 'utf8');
const delim = process.argv[3];
const lineReader = new LineReader(f, delim);
lineReader.on('line', (line)=> console.log(line));

Aquí hay una versión aún más corta, sin las estadísticas, en 19 líneas:

class LineReader extends require('events').EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.leftover = '';
        f.on('data', (chunk)=>{
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) 
                lines.pop();
            for (let l of lines)
                this.emit('line', l);
        });
    }
}
javajosh
fuente
0
const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, data) => {
var innerContent;
    console.log("Asynchronous read: " + data.toString());
    const lines = data.toString().split('\n')
    for (let line of lines)
        innerContent += line + '<br>';


});
Arindam
fuente
0

Envuelvo toda la lógica del procesamiento diario de líneas como un módulo npm: line-kit https://www.npmjs.com/package/line-kit

// example
var count = 0
require('line-kit')(require('fs').createReadStream('/etc/issue'),
                    (line) => { count++; },
                    () => {console.log(`seen ${count} lines`)})

Joyer
fuente
-1

Utilizo el siguiente código de las líneas de lectura después de verificar que no es un directorio y que no está incluido en la lista de archivos, no es necesario verificarlo.

(function () {
  var fs = require('fs');
  var glob = require('glob-fs')();
  var path = require('path');
  var result = 0;
  var exclude = ['LICENSE',
    path.join('e2e', 'util', 'db-ca', 'someother-file'),
    path.join('src', 'favicon.ico')];
  var files = [];
  files = glob.readdirSync('**');

  var allFiles = [];

  var patternString = [
    'trade',
    'order',
    'market',
    'securities'
  ];

  files.map((file) => {
    try {
      if (!fs.lstatSync(file).isDirectory() && exclude.indexOf(file) === -1) {
        fs.readFileSync(file).toString().split(/\r?\n/).forEach(function(line){
          patternString.map((pattern) => {
            if (line.indexOf(pattern) !== -1) {
              console.log(file + ' contain `' + pattern + '` in in line "' + line +'";');
              result = 1;
            }
          });
        });
      }
    } catch (e) {
      console.log('Error:', e.stack);
    }
  });
  process.exit(result);

})();
Aniruddha Das
fuente
-1

He revisado todas las respuestas anteriores, todas ellas usan una biblioteca de terceros para resolverlo. Tiene una solución simple en la API de Node. p.ej

const fs= require('fs')

let stream = fs.createReadStream('<filename>', { autoClose: true })

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))
mrcode
fuente