J'essaie de lire un fichier volumineux, une ligne à la fois. J'ai trouvé une question sur Quora qui traitait du sujet, mais il me manque des liens pour que tout se passe bien.
var Lazy=require("lazy");
new Lazy(process.stdin)
.lines
.forEach(
function(line) {
console.log(line.toString());
}
);
process.stdin.resume();
Ce que j'aimerais comprendre, c'est comment lire une ligne à la fois dans un fichier au lieu de STDIN comme dans cet exemple.
J'ai essayé:
fs.open('./VeryBigFile.csv', 'r', '0666', Process);
function Process(err, fd) {
if (err) throw err;
// DO lazy read
}
mais ça ne marche pas. Je sais qu'à la limite je pourrais utiliser quelque chose comme PHP, mais j'aimerais comprendre ceci.
Je ne pense pas que l’autre réponse fonctionnerait, car le fichier est beaucoup plus volumineux que le serveur sur lequel je l’utilise a de la mémoire.
Depuis Node.js v0.12 et depuis Node.js v4.0.0, il existe un module stable readline core. Voici le moyen le plus simple de lire les lignes d'un fichier, sans aucun module externe:
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('file.in')
});
lineReader.on('line', function (line) {
console.log('Line from file:', line);
});
La dernière ligne est lue correctement (à partir de Node v0.12 ou version ultérieure), même s’il n’ya pas de \n
final.
UPDATE: cet exemple a été ajouté à la documentation officielle de l'API de Node .
Pour une opération aussi simple, il ne devrait y avoir aucune dépendance à l'égard des modules tiers. Allez-y doucement.
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);
});
Vous n'avez pas à open
le fichier, mais vous devez plutôt créer une ReadStream
.
Puis passez ce flux à Lazy
il existe un module très agréable pour lire un fichier ligne par ligne, il s'appelle line-reader
avec cela vous écrivez simplement:
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
}
});
vous pouvez même itérer le fichier avec une interface "de style Java", si vous avez besoin de plus de contrôle:
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);
})
Vieux sujet, mais ça marche:
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. Pas besoin d'un module externe.
Vous pouvez toujours lancer votre propre lecteur de ligne. Je n'ai pas encore analysé cet extrait, mais il divise correctement le flux entrant de morceaux en lignes sans le '\ n' suivi.
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();
Je suis arrivé à cela lorsque je travaillais sur un script d’analyse rapide des journaux qui devait accumuler des données lors de l’analyse des journaux et j’ai pensé qu’il serait agréable d’essayer de le faire en utilisant js et node au lieu d’utiliser Perl ou bash.
Quoi qu’il en soit, j’estime que les petits scripts nodejs devraient être autonomes et ne pas dépendre de modules tiers. Ainsi, après avoir lu toutes les réponses à cette question, chacune utilisant divers modules pour gérer l’analyse de ligne, une solution nodej 13 native pour SLOC pourrait être intéressante.
Avec le module opérateur :
var carrier = require('carrier');
process.stdin.resume();
carrier.carry(process.stdin, function(line) {
console.log('got one line: ' + line);
});
Modifier:
Utilisez un flux transform .
Avec BufferedReader , vous pouvez lire des lignes.
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 ();
Je me suis retrouvé avec une fuite de mémoire massive en utilisant Lazy pour lire ligne par ligne lorsque vous essayez de traiter ces lignes et de les écrire dans un autre flux en raison du fonctionnement de drain/pause/resume dans les nœuds (voir: http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (j'adore ce mec). Je n'ai pas suffisamment regardé Lazy pour comprendre pourquoi, mais je ne pouvais pas suspendre mon flux de lecture pour permettre un drain sans quitter Lazy.
J'ai écrit le code pour traiter des fichiers csv massifs en documents xml, vous pouvez voir le code ici: https://github.com/j03m/node-csv2xml
Si vous exécutez les révisions précédentes avec Lazy Line, cela fuit. La dernière révision ne coule pas du tout et vous pouvez probablement l'utiliser comme base pour un lecteur/processeur. Bien que j'ai des trucs personnalisés là-dedans.
Edit: Je suppose que je devrais également noter que mon code avec Lazy a bien fonctionné jusqu'à ce que je me suis retrouvé à écrire des fragments xml assez volumineux qui drainent/mettent en pause/reprennent parce que c'est une nécessité. Pour les petits morceaux c'était bien.
Dans la plupart des cas, cela devrait suffire:
const fs = require("fs")
fs.readFile('./file', 'utf-8', (err, file) => {
const lines = file.split('\n')
for (let line of lines)
console.log(line)
});
J'étais frustré par le manque de solution globale pour cela, alors j'ai monté ma propre tentative ( git / npm ). Liste de fonctionnalités copiées-collées:
NIH? Tu décides :-)
Depuis la publication de ma réponse initiale, j’ai constaté que split est un module de nœud très facile à utiliser pour la lecture de lignes dans un fichier; Qui accepte également des paramètres optionnels.
var split = require('split');
fs.createReadStream(file)
.pipe(split())
.on('data', function (line) {
//each chunk now is a seperate line!
});
N'a pas testé sur de très gros fichiers. Faites-nous savoir si vous le faites.
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("++++++++++++++++++++")
})
Je voulais aborder ce même problème, en gros ce que serait Perl:
while (<>) {
process_line($_);
}
Mon cas d'utilisation était juste un script autonome, pas un serveur, donc synchrone allait bien. Ce sont mes critères:
C’est un projet qui me permet de comprendre le code de type de script de bas niveau dans node.js et de décider de sa viabilité en remplacement d’autres langages de script tels que Perl.
Après des efforts surprenants et quelques faux départs, voici le code que j'ai créé. C'est assez rapide mais moins trivial que ce à quoi je m'attendais: (à la fourchette sur 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;
}
}
}
Il pourrait probablement être nettoyé plus loin, c'était le résultat d'essais et d'erreurs.
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();
}
J'ai eu le même problème et est venu avec la solution ci-dessus .___. Ressemble à d'autres, mais est un Sync et peut lire des fichiers volumineux très rapidement
Espère que cela aide
Lecteur de ligne basé sur un générateur: 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());
}
});
});
Si vous voulez lire un fichier ligne par ligne et l'écrire dans un autre:
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');
});
};
Une autre solution consiste à exécuter la logique via l'exécuteur séquentiel nsynjs . Il lit les fichiers ligne par ligne à l'aide du module readline du nœud. Il n'utilise pas de promesses ni de récursivité. Il ne risque donc pas d'échouer sur des fichiers volumineux. Voici à quoi ressemblera le code:
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');
});
Le code ci-dessus est basé sur cet exemple: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js
Mise à jour en 2019
Un excellent exemple est déjà publié sur la documentation officielle de Nodejs. ici
Cela nécessite que la dernière version de Nodejs soit installée sur votre ordinateur. > 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();
J'ai un petit module qui le fait bien et qui est utilisé par pas mal de projets npm readline Note dans le noeud v10, il y a un module natif readline, j'ai donc republié mon module comme linebyline https://www.npmjs.com/package/linebyline
si vous ne voulez pas utiliser le module, la fonction est très 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);
J'enveloppe toute la logique du traitement quotidien des lignes en tant que module npm: 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>';
});
j'utilise ceci:
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
utilisez cette fonction sur un flux et écoutez les événements de ligne qui seront émis.
gr-
Alors que vous devriez probablement utiliser le module readline
comme le suggère la réponse principale, readline
semble être orienté vers les interfaces de ligne de commande plutôt que vers la lecture de ligne. C'est aussi un peu plus opaque en ce qui concerne la mise en mémoire tampon. (Toute personne ayant besoin d’un lecteur orienté ligne en continu voudra probablement modifier la taille des tampons). Le module readline comprend environ 1 000 lignes, tandis que cela correspond à 34 statistiques et tests.
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));
Voici une version encore plus courte, sans les statistiques, à 19 lignes:
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);
});
}
}