[javascript] Lire un fichier une ligne à la fois dans node.js?


Answers

Pour une opération aussi simple, il ne devrait pas y avoir de dépendance aux 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);
});
Question

J'essaie de lire un gros fichier 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 corresponde.

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

Le bit que je voudrais comprendre est 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 qu'à la rigueur je pourrais me rabattre sur l'utilisation de PHP, mais je voudrais comprendre cela.

Je ne pense pas que l'autre solution fonctionnerait car le fichier est beaucoup plus volumineux que le serveur pour lequel je l'utilise.




Vous pouvez toujours lancer votre propre lecteur de ligne. Je n'ai pas encore évalué cet extrait, mais il divise correctement le flux entrant de morceaux en lignes sans le \ 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();

J'ai trouvé cela en travaillant sur un script d'analyse rapide du journal qui devait accumuler des données pendant l'analyse du journal et j'ai pensé qu'il serait bien d'essayer de le faire en utilisant js et node au lieu d'utiliser perl ou bash.

Quoi qu'il en soit, je pense que les petits scripts nodejs devraient être autonomes et ne pas reposer sur des modules tiers. Après avoir lu toutes les réponses à cette question, en utilisant différents modules pour gérer l'analyse des lignes, une solution native peut être intéressante.




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-




Je voulais aborder ce même problème, essentiellement ce que serait Perl:

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

Mon cas d'utilisation était juste un script autonome, pas un serveur, donc synchrone était bien. Ce sont mes critères:

  • Le code synchrone minimal pouvant être réutilisé dans de nombreux projets.
  • Aucune limite sur la taille du fichier ou le nombre de lignes.
  • Pas de limites sur la longueur des lignes.
  • Capable de gérer un Unicode complet en UTF-8, y compris les caractères au-delà du BMP.
  • Capable de gérer les terminaisons de ligne nix et Windows (Mac ancien n'est pas nécessaire pour moi).
  • Caractère (s) des fins de ligne à inclure dans les lignes.
  • Capable de gérer la dernière ligne avec ou sans caractères de fin de ligne.
  • N'utilisez aucune bibliothèque externe non incluse dans la distribution node.js.

C'est un projet pour moi d'avoir une idée du code de type de script de bas niveau dans node.js et de décider comment il est viable en remplacement d'autres langages de script comme Perl.

Après un nombre impressionnant d'efforts et quelques faux départs, c'est le code que j'ai trouvé. C'est plutôt rapide mais moins trivial que ce à quoi je m'attendais: (le fork 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.




il y a un très bon module pour lire un fichier ligne par ligne, ça 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);
    });
  }
});



Modifier:

Utilisez un flux de transformation .

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



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("++++++++++++++++++++")
})



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

  });
});



J'utilise ci-dessous le code les lignes de lecture après vérifier que ce n'est pas un répertoire et son non inclus dans la liste des fichiers ne doit pas être 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);

})();



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 aSync et peut lire des fichiers volumineux très rapidement

Espère que cela aide




Je me suis retrouvé avec une énorme fuite de mémoire en utilisant Lazy pour lire ligne par ligne en essayant de traiter ces lignes et de les écrire dans un autre flux en raison de la façon dont fonctionne le drain (voir: http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (j'aime ce gars btw)). Je n'ai pas regardé assez attentivement Lazy pour comprendre exactement pourquoi, mais je ne pouvais pas mettre en pause mon flux de lecture pour permettre un vidage sans sortie Lazy.

J'ai écrit le code pour traiter des fichiers csv massifs dans docs xml, vous pouvez voir le code ici: https://github.com/j03m/node-csv2xml

Si vous exécutez les révisions précédentes avec la ligne Lazy, cela fuit. La dernière révision ne fuit 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 aussi noter que mon code avec Lazy a bien fonctionné jusqu'à ce que je me retrouve à écrire des fragments xml assez importants qui drainent / mettent en pause / reprennent parce qu'une nécessité. Pour les petits morceaux c'était bien.




Related