Lire un fichier ligne par ligne dans le nœud.js?

j'essaie de lire un grand fichier une ligne à la fois. J'ai trouvé une question sur Quora qui traitait du sujet mais je manque quelques connexions pour faire le tout s'emboîter ensemble.

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

Le peu que j'aimerais savoir comment je pourrais lire une ligne à la fois à partir d'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 que dans une pincée je pourrais retomber à utiliser quelque chose comme PHP, mais je voudrais comprendre cela.

Je ne pense pas que l'autre réponse fonctionnerait car le fichier est beaucoup plus grand que le serveur sur lequel je l'exécute a de la mémoire.

434
demandé sur jonrsharpe 2011-05-27 22:49:48

25 réponses

Depuis Noeud.js v0.12 et à partir du noeud.js v4.0.0, Il y a un module stable readline core module. Voici la façon la plus facile de lire les lignes à partir 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 du noeud v0.12 ou plus), même s'il n'y a pas de \n final .

UPDATE : cet exemple a été ajouté à la documentation officielle de L'API de Node .

571
répondu Dan Dascalescu 2018-08-29 21:37:30

pour une opération aussi simple, il ne devrait pas y avoir de dépendance aux modules tiers. Aller simple.

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);
});
148
répondu kofrasa 2017-02-07 15:51:09

vous n'avez pas à open le fichier, mais à la place, vous devez créer un ReadStream .

fs.createReadStream

puis passer ce ruisseau à Lazy

61
répondu Raynos 2012-01-22 05:16:18

il y a un très beau module 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 "java-style", 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);
    });
  }
});
30
répondu polaretto 2018-04-15 16:55:11

Vieux sujet, mais cela fonctionne:

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 de module externe.

18
répondu nf071590 2014-04-25 19:24:16

vous pouvez toujours lancer votre propre lecteur de lignes. Je n'ai pas encore comparé cet extrait, mais il divise correctement le flux entrant de morceaux dans les lignes sans la traînée '\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();

j'ai trouvé cela en travaillant sur un script d'analyse de journal rapide qui avait besoin d'accumuler des données pendant l'analyse de journal et j'ai senti qu'il serait agréable d'essayer de faire cela en utilisant js et node au lieu d'utiliser perl ou bash.

quoi qu'il en soit, je pense que les petits scripts nodejs doivent être autonomes et ne pas s'appuyer sur des modules tiers, donc après avoir lu toutes les réponses à cette question, chacun utilisant divers modules pour gérer l'analyse en ligne, une solution nodejs natif 13 SLOC pourrait être intéressante .

16
répondu Ernelli 2013-08-15 12:35:51
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})
16
répondu John Williams 2018-09-23 21:19:47

avec le module de support :

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});
12
répondu Touv 2013-04-28 23:07:55

j'ai fini avec une fuite de mémoire massive, massive en utilisant paresseux pour lire ligne par ligne en essayant ensuite de traiter ces lignes et de les écrire à un autre flux en raison de la façon drain / pause / reprendre dans les travaux de noeud (voir: http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams / (i love this guy btw)). Je n'ai pas assez regardé Lazy pour comprendre exactement pourquoi, mais je ne pouvais pas mettre en pause Mon flux de lecture pour permettre un drain sans Paresseux de la sortie.

j'ai écrit le code pour traiter des fichiers csv massifs dans des 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 il fuit. La dernière révision ne fuit pas du tout et vous pouvez probablement l'utiliser comme base pour un lecteur/processeur. Si j'ai quelques trucs là.

Edit: je suppose que je devrais aussi noter que mon code avec Lazy travaillé très bien jusqu'à ce que je me retrouve à écrire des fragments xml assez grand que drain/pause/reprendre parce qu'une nécessité. Pour des morceaux plus petits, c'était bien.

8
répondu j03m 2011-11-09 04:06:08

Edit:

Utiliser un le flux de transformation .


avec un BufferedReader vous pouvez lire les 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 ();
8
répondu Gabriel Llamas 2014-06-08 06:31:57

j'ai été frustré par l'absence d'une solution globale pour cela, j'ai donc monté ma propre tentative ( git / mnp ). Liste des caractéristiques copiée:

  • traitement de ligne interactif (basé sur la fonction de rappel, aucun chargement du fichier entier en mémoire vive)
  • optionnellement, retourner toutes les lignes dans un tableau (détaillé ou en mode brut)
  • interrompre de façon interactive la diffusion en continu ou jouer traitement similaire à une carte / filtre
  • Détecter toute nouvelle ligne de la convention (PC/Mac/Linux)
  • Correcte des expressions du folklore et de la dernière ligne de traitement
  • la bonne gestion de multi-octets de caractères UTF-8
  • extraire l'information sur l'offset et la longueur des octets par ligne
  • accès Aléatoire, à l'aide de la ligne de base ou d'octets à base de décalages
  • cartographier automatiquement les informations de décalage de ligne, pour accélérer aléatoire accès 151980920"
  • Zéro "dépendances de 151980920"
  • Essais

NIH? Vous décidez: -)

6
répondu panta82 2014-05-20 22:01:10

depuis la publication de ma réponse originale, j'ai constaté que split est un module de noeud très facile à utiliser pour la lecture de ligne 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 été testé sur de très gros fichiers. Laissez-nous savoir si vous le faites.

6
répondu nf071590 2015-09-21 19:36:01

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)
});
6
répondu Dorian 2017-02-08 12:47:21
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("++++++++++++++++++++")
})
5
répondu user531097 2012-06-13 06:03:40

je voulais m'attaquer à ce même problème, essentiellement ce qu'en Perl serait:

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

mon cas d'utilisation était juste un script autonome, pas un serveur, donc synchrone était très bien. Voici mes critères:

  • le code synchrone minimal qui pourrait être réutilisé dans de nombreux projets.
  • Pas de limites sur la taille du fichier ou le nombre de lignes.
  • Pas de limites sur la longueur des lignes.
  • capable de gérer L'Unicode complet en UTF-8, y compris les caractères au-delà de la BMP.
  • capable de gérer *nix et les terminaisons de ligne Windows (Mac à l'ancienne pas nécessaire pour moi).
  • fin de ligne caractère (s) à inclure dans les lignes.
  • capable de gérer la dernière ligne avec ou sans caractères de fin de ligne.
  • ne pas utiliser de bibliothèques externes non incluses dans le noeud.js de distribution.

il s'agit d'un projet pour moi d'obtenir un aperçu de bas niveau de code de type de script dans le noeud.js et de décider comment il est viable comme un remplacement pour d'autres langages de script comme Perl.

après un effort surprenant et quelques faux départs c'est le code que j'ai inventé. C'est assez rapide mais moins trivial que je ne l'aurais attendu: (bifurcation 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és en outre, il a été le résultat d'essai et erreur.

4
répondu hippietrail 2013-04-23 10:32:01

Générateur de ligne basée sur le lecteur: 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());
    }

  });
});
2
répondu neurosnap 2015-06-21 13:40:24

si vous voulez lire un fichier ligne par ligne et écrire ceci dans une 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');
   });
};
2
répondu Thami Bouchnafa 2015-09-02 06:16:32
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 j'ai trouvé la solution ci-dessus ressemble à d'autres, mais est aSync et peut lire de grands fichiers très rapidement

espère que cela aidera

2
répondu user2056154 2016-05-01 10:10:39

j'ai un petit module qui fait cela bien et est utilisé par un certain nombre d'autres projets npm readline Note thay dans le noeud v10 Il ya un module natif readline donc j'ai 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);
1
répondu Maleck13 2015-01-17 10:13:49
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>';


});
1
répondu Arindam 2017-05-30 10:49:31

une autre solution consiste à exécuter logic via l'exécuteur séquentiel nsynjs . Il lit le fichier ligne par ligne en utilisant le module de lecture de noeud, et il n'utilise pas les promesses ou la récursion, donc ne va pas échouer sur les grands fichiers. 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

1
répondu amaksr 2017-06-14 00:46:15

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 stream et écoutez les événements de ligne qui seront émis.

gr -

0
répondu Elmer 2013-02-10 20:57:39

alors que vous devriez probablement utiliser le module readline comme le suggère la réponse du haut, readline semble être orienté vers les interfaces de ligne de commande plutôt que la lecture en ligne. Il est également un peu plus opaque en ce qui concerne le tampon. (Toute personne qui a besoin d'un lecteur orienté ligne de diffusion voudra probablement modifier la taille des tampons). Le module readline est d'environ 1000 lignes alors que celui-ci, avec les statistiques et les tests, est 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));

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);
        });
    }
}
0
répondu javajosh 2017-03-17 22:32:57

j'utilise ci-dessous le code des lignes de lecture après avoir vérifié que ce n'est pas un répertoire et que ce n'est pas inclus dans la liste des fichiers à vérifier.

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

})();
0
répondu Aniruddha Das 2017-09-11 17:44:32

j'ai regardé à travers toutes les réponses ci-dessus, chacun d'eux utilisent une bibliothèque tierce pour résoudre. Il y a une solution simple dans L'API de Node. E. g

const fs= require('fs')

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

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))
-1
répondu mrcode 2018-05-02 14:40:12