javascript - txt - Leer un archivo una línea a la vez en node.js?
node read file (25)
Con el módulo portador :
var carrier = require(''carrier'');
process.stdin.resume();
carrier.carry(process.stdin, function(line) {
console.log(''got one line: '' + line);
});
Estoy tratando de leer un archivo grande una línea a la vez. Encontré una pregunta en Quora que trataba el tema, pero me faltan algunas conexiones para hacer que todo encaje bien.
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 puedo leer una línea a la vez de un archivo en lugar de STDIN como en esta muestra.
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 volver 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 y tiene memoria.
Desde Node.js v0.12 y a partir de Node.js v4.0.0, existe un módulo básico de línea de lectura estable. Esta es la forma más fácil de leer líneas de un archivo, sin ningún módulo externo:
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 un /n
final.
ACTUALIZACIÓN : este ejemplo se ha agregado a la documentación oficial de la API de Node .
Desde que publiqué mi respuesta original, descubrí que split 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.
Editar:
Utilice un flujo 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 ();
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)
});
Hay un módulo muy bueno para leer un archivo línea por línea, se llama line-reader
con eso 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);
});
}
});
He revisado todas las respuestas anteriores, todas ellas utilizan una biblioteca de terceros para resolverlo. Es tener 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'')
}))
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());
}
});
});
Me sentí frustrado por la falta de una solución integral para esto, así que npm mi propio intento ( git / npm ). Copia pegada lista de características:
- Procesamiento de línea interactivo (basado en devolución de llamada, sin cargar el archivo completo en la RAM)
- Opcionalmente, devolver todas las líneas en una matriz (modo detallado o sin procesar)
- Interrumpa la transmisión de manera interactiva, o realiza un mapa / filtro como el procesamiento
- Detectar cualquier convención de nueva línea (PC / Mac / Linux)
- Corregir tratamiento eof / last line.
- Manejo correcto de los caracteres UTF-8 de múltiples bytes.
- Recuperar la información de desplazamiento de byte y longitud de byte por línea
- Acceso aleatorio, utilizando desplazamientos basados en línea o en bytes.
- Asigne automáticamente información de desplazamiento de línea para acelerar el acceso aleatorio
- Cero dependencias
- Pruebas
NIH? Tú decides :-)
No tiene que open
el archivo, pero en su lugar, debe crear un ReadStream
.
Luego pasa esa corriente a Lazy
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 readline del nodo, y no usa promesas o recursión, por lo tanto no falla en archivos grandes. Aquí es cómo 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 ejemplo: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js
Para una operación tan simple, no debería haber ninguna dependencia en los 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);
});
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 la sincronización estaba bien. Estos fueron mis criterios:
- El mínimo código síncrono 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 * nix y finales de línea de Windows (Mac de estilo antiguo no es necesario para mí).
- Caracteres de los finales de línea que se incluirán en las líneas.
- Capaz de manejar la última línea con o sin caracteres de fin de línea.
- No utilice ninguna biblioteca externa no incluida en la distribución node.js.
Este es un proyecto para mí para tener una idea del código de tipo de scripts de bajo nivel en node.js y decidir qué tan viable es como un reemplazo para otros lenguajes de scripts como Perl.
Después de una sorprendente cantidad de esfuerzo y un par de inicios falsos, este es el código que se me ocurrió. Es bastante rápido, pero menos trivial de lo que hubiera esperado: (dalo 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 ser limpiado aún más, fue el resultado de prueba y error.
Si bien probablemente debería usar el módulo readline
como sugiere la respuesta principal, parece que readline
está 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á modificar el tamaño de los búferes). El módulo readline es ~ 1000 líneas, mientras que este, con estadísticas y pruebas, es de 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 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);
});
}
}
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'');
});
};
Siempre puedes rodar tu propia línea de lector. Todavía no he evaluado este fragmento de código, pero divide correctamente el flujo entrante de fragmentos en líneas sin el final ''/ n''
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 al trabajar 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 hacer esto utilizando js y node en lugar de usar perl o bash.
De todos modos, creo que los scripts de nodejs pequeños deberían ser autónomos y no confiar en módulos de terceros, por lo que, después de leer todas las respuestas a esta pregunta, cada una de las cuales utiliza varios módulos para manejar el análisis de líneas, una solución de nodejs nativo de 13 SLOC puede ser interesante.
Tengo un pequeño módulo que hace esto bien y es usado por muchos otros proyectos. Npm readline Note que en el nodo v10 hay un módulo de readline nativo, así que reedité mi módulo como linebyline https://www.npmjs.com/package/linebyline
Si no quieres usar 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);
Terminé con una pérdida masiva y masiva de memoria que usaba 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 (consulte: http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (amo a este chico por cierto)). No he mirado a Lazy lo suficientemente de cerca como para entender exactamente por qué, pero no pude detener mi flujo de lectura para permitir un drenaje sin que salga Lazy.
Escribí el código para procesar archivos csv masivos en documentos XML, puede ver el código aquí: https://github.com/j03m/node-csv2xml
Si ejecuta las revisiones anteriores con la línea perezosa se filtra. La última revisión no se filtra en absoluto y probablemente pueda usarla como base para un lector / procesador. Aunque tengo algunas cosas personalizadas allí.
Edición: Supongo que también debo tener en cuenta que mi código con Lazy funcionó bien hasta que me encontré escribiendo fragmentos xml lo suficientemente grandes para drenar / pausar / reanudar debido a una necesidad. Para trozos más pequeños estaba bien.
Utilizo el siguiente código para leer las líneas después de verificar que no es un directorio y no está incluido en la lista de archivos y que no es necesario verificar.
(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);
})();
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
})
Sencillo. No es necesario un módulo externo.
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-
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>'';
});
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("++++++++++++++++++++")
})
require(''fs'').readFileSync(''file.txt'', ''utf-8'').split(//r?/n/).forEach(function(line){
console.log(line);
})
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ó la solución anterior que parece similar a la de otros, pero es aSync y puede leer archivos grandes muy rápidamente
Espera que esto ayude