Ich versuche, eine große Datei Zeile für Zeile zu lesen. Ich fand eine Frage zu Quora , die sich mit dem Thema befasste, aber es fehlen einige Verbindungen, um das Ganze zusammenzufügen.
var Lazy=require("lazy");
new Lazy(process.stdin)
.lines
.forEach(
function(line) {
console.log(line.toString());
}
);
process.stdin.resume();
Das bisschen, das ich herausfinden möchte, ist, wie ich zeilenweise eine Datei aus einer Datei anstelle von STDIN wie in diesem Beispiel lesen kann.
Ich habe es versucht:
fs.open('./VeryBigFile.csv', 'r', '0666', Process);
function Process(err, fd) {
if (err) throw err;
// DO lazy read
}
aber es funktioniert nicht Ich weiß, dass ich zur Not auf etwas wie PHP zurückgreifen könnte, aber ich würde das gerne herausfinden.
Ich glaube nicht, dass die andere Antwort funktionieren würde, da die Datei viel größer ist als der Server, auf dem ich sie ausgeführt habe, über Speicher verfügt.
Seit Node.js v0.12 und ab Node.js v4.0.0 gibt es ein stabiles readline core-Modul. Es ist der einfachste Weg, Zeilen aus einer Datei ohne externe Module zu lesen:
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('file.in')
});
lineReader.on('line', function (line) {
console.log('Line from file:', line);
});
Die letzte Zeile wird korrekt gelesen (ab Node v0.12 oder höher), auch wenn kein endgültiger \n
vorhanden ist.
UPDATE: Dieses Beispiel wurde der offiziellen API-Dokumentation von Node hinzugefügt .
Für eine solche einfache Operation sollte keine Abhängigkeit von Fremdmodulen bestehen. Geh einfach.
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);
});
Sie müssen die Datei nicht open
, sondern eine ReadStream
erstellen.
Dann übergeben Sie diesen Stream an Lazy
es gibt ein sehr schönes Modul, um eine Datei Zeile für Zeile zu lesen. Es heißt line-reader
damit schreiben Sie einfach nur:
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
}
});
sie können die Datei sogar mit einer "Java-Stil" -Schnittstelle iterieren, wenn Sie mehr Kontrolle benötigen:
lineReader.open('file.txt', function(reader) {
if (reader.hasNextLine()) {
reader.nextLine(function(line) {
console.log(line);
});
}
});
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
console.log(line);
})
Altes Thema, aber das funktioniert:
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
})
Einfach. Kein externes Modul erforderlich.
Sie können immer Ihren eigenen Zeilenleser rollen. Ich habe dieses Snippet noch nicht getestet, aber es teilt den ankommenden Stream von Chunks korrekt in Zeilen ohne das nachfolgende '\ n' auf.
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();
Ich kam dazu, als ich an einem schnellen Log-Parsing-Skript arbeitete, das während des Log-Parsings Daten sammeln musste, und ich dachte, es wäre schön, dies mit js und node anstelle von Perl oder bash zu versuchen.
Ich denke jedoch, dass kleine nodejs-Skripts in sich geschlossen sein sollten und nicht auf Modulen von Drittanbietern angewiesen sind. Wenn Sie also alle Antworten auf diese Frage gelesen haben und jeweils verschiedene Module für das Zeilen-Parsing verwenden, könnte eine native SLOC-Lösung mit 13 SLOC von Interesse sein.
Mit dem Trägermodul :
var carrier = require('carrier');
process.stdin.resume();
carrier.carry(process.stdin, function(line) {
console.log('got one line: ' + line);
});
Bearbeiten:
Verwenden Sie einen transform stream .
Mit einem BufferedReader können Sie Zeilen lesen.
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 ();
Am Ende kam es zu einem massiven, massiven Speicherverlust. Ich habe Lazy verwendet, um Zeile für Zeile zu lesen, wenn ich dann versuche, diese Zeilen zu verarbeiten und sie in einen anderen Stream zu schreiben, da Drain/Pause/Resume im Knoten funktioniert (siehe: http://elegantcode.com/2011/04/06/taking-baby-steps-mit-node-js-pumping-data-between-streams/ (ich liebe diesen Kerl übrigens)). Ich habe Lazy nicht genau genug angesehen, um genau zu verstehen, warum, aber ich konnte meinen Lesestrom nicht unterbrechen, um einen Abfluss zuzulassen, ohne dass Lazy beendet wurde.
Ich habe den Code geschrieben, um massive CSV-Dateien in XML-Dokumente zu verarbeiten. Den Code finden Sie hier: https://github.com/j03m/node-csv2xml
Wenn Sie die vorherigen Revisionen mit der Lazy-Zeile ausführen, tritt ein Leck auf. Die neueste Version tritt nicht aus, und Sie können sie wahrscheinlich als Grundlage für einen Leser/Prozessor verwenden. Obwohl ich ein paar benutzerdefinierte Sachen habe.
Edit: Ich denke, ich sollte auch beachten, dass mein Code mit Lazy gut funktioniert hat, bis ich fand, dass ich groß genug XML-Fragmente schrieb, die aus Notwendigkeit abtropfen/pausieren/wieder aufnehmen. Für kleinere Brocken war es gut.
In den meisten Fällen sollte dies ausreichen:
const fs = require("fs")
fs.readFile('./file', 'utf-8', (err, file) => {
const lines = file.split('\n')
for (let line of lines)
console.log(line)
});
Ich war frustriert über das Fehlen einer umfassenden Lösung, daher habe ich meinen eigenen Versuch zusammengestellt ( git / npm ). Kopierte Liste der Features:
NIH? Du entscheidest :-)
Seit ich meine ursprüngliche Antwort veröffentlicht habe, habe ich festgestellt, dass split ein sehr einfach zu verwendendes Knotenmodul zum Lesen von Zeilen in einer Datei ist. Die auch optionale Parameter akzeptiert.
var split = require('split');
fs.createReadStream(file)
.pipe(split())
.on('data', function (line) {
//each chunk now is a seperate line!
});
Wurde nicht mit sehr großen Dateien getestet. Lass es uns wissen.
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("++++++++++++++++++++")
})
Ich wollte das gleiche Problem lösen, was im Grunde in Perl wäre:
while (<>) {
process_line($_);
}
Mein Anwendungsfall war nur ein eigenständiges Skript, kein Server, so synchron war alles in Ordnung. Dies waren meine Kriterien:
Dies ist ein Projekt für mich, um ein Gefühl für den Code-Code der unteren Ebene in node.js zu bekommen und zu entscheiden, wie praktikabel es ist, um andere Skriptsprachen wie Perl zu ersetzen.
Nach einem überraschenden Aufwand und einigen Fehlstarts ist dies der Code, den ich mir ausgedacht habe. Es ist ziemlich schnell, aber weniger trivial als ich erwartet hätte: (Fork it on 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;
}
}
}
Es könnte wahrscheinlich weiter aufgeräumt werden, es war das Ergebnis von Versuch und Irrtum.
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();
}
Ich hatte das gleiche Problem und kam zu der oben genannten Lösung. __ sieht ähnlich aus, ist aber aSync und kann große Dateien sehr schnell lesen
Hoffentlich hilft das
Generatorenbasierter Zeilenleser: 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());
}
});
});
Wenn Sie eine Datei Zeile für Zeile lesen und diese in eine andere schreiben wollen:
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');
});
};
Eine andere Lösung besteht darin, die Logik über den sequentiellen Executor nsynjs auszuführen. Er liest die Datei Zeile für Zeile mit dem Node-Readline-Modul und verwendet keine Versprechungen oder Rekursionen. Daher wird bei großen Dateien kein Fehler auftreten. So wird der Code aussehen:
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');
});
Der obige Code basiert auf diesem Beispiel: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js
Update im Jahr 2019
Ein großartiges Beispiel ist bereits in der offiziellen Nodejs-Dokumentation veröffentlicht. Hier
Dies setzt voraus, dass die neuesten Nodejs auf Ihrem Computer installiert sind. > 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();
Ich habe ein kleines Modul, das dies gut macht und von vielen anderen Projekten verwendet wird npm readline Hinweis Wenn im Knoten v10 ein natives Readline-Modul vorhanden ist, habe ich mein Modul erneut als linebyline https: // www veröffentlicht. npmjs.com/package/linebyline
wenn Sie das Modul nicht verwenden möchten, ist die Funktion sehr einfach:
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);
Ich füge die gesamte Logik der täglichen Zeilenverarbeitung als npm-Modul zusammen: line-kithttps://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`)})
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>';
});
ich benutze das:
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
verwenden Sie diese Funktion für einen Stream und überwachen Sie die Zeilenereignisse, die ausgegeben werden.
gR-
Während Sie wahrscheinlich das Modul readline
verwenden sollten, wie die erste Antwort nahelegt, scheint readline
eher auf Befehlszeilenschnittstellen als auf das Lesen von Zeilen ausgerichtet zu sein. Es ist auch etwas undurchsichtiger in Bezug auf die Pufferung. (Wer ein Streaming-Line-orientiertes Lesegerät benötigt, wird wahrscheinlich die Puffergröße anpassen wollen). Das Readline-Modul ist ~ 1000 Zeilen, während dies bei Statistiken und Tests 34 ist.
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));
Hier ist eine noch kürzere Version ohne Statistik mit 19 Zeilen:
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);
});
}
}