703 votes

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

J'essaie de lire un grand fichier une ligne à la fois. J'ai trouvé une question sur Quora qui traitaient du sujet, mais il me manque des liens pour que l'ensemble soit cohérent.

 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 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'en cas de difficulté, je pourrais me rabattre sur quelque chose comme PHP, mais j'aimerais trouver une solution.

Je ne pense pas que l'autre réponse puisse fonctionner car le fichier est beaucoup plus volumineux que ce que le serveur sur lequel je l'exécute peut contenir.

3 votes

Il s'avère que cela est assez difficile en utilisant uniquement des outils de bas niveau. fs.readSync() . Il est possible de lire des octets binaires dans un tampon, mais il n'existe aucun moyen simple de traiter les caractères UTF-8 ou UTF-16 partiels sans inspecter le tampon avant de le traduire en chaînes JavaScript et de rechercher les EOL. Le site Buffer() ne dispose pas d'un ensemble aussi riche de fonctions pour opérer sur ses instances que les chaînes de caractères natives, mais les chaînes de caractères natives ne peuvent pas contenir de données binaires. Il me semble que l'absence d'un moyen intégré de lire des lignes de texte à partir de fichiers arbitraires est une véritable lacune dans node.js.

5 votes

Les lignes vides lues par cette méthode sont converties en une ligne avec un seul 0 (code caractère réel pour 0). J'ai dû modifier cette ligne : if (line.length==1 && line[0] == 48) special(line);

2 votes

On peut aussi utiliser le paquet "ligne par ligne" qui fait parfaitement l'affaire.

173voto

kofrasa Points 501

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

4 votes

Il y a un problème avec cette solution. Si vous utilisez your.js <lines.txt vous n'obtenez pas la dernière ligne. Si elle n'a pas de ' \n à la fin bien sûr.

0 votes

Le site readline se comporte de manière vraiment bizarre pour un programmeur Unix/Linux expérimenté.

12 votes

rd.on("close", ..); peut être utilisé comme un callback (se produit lorsque toutes les lignes sont lues)

65voto

Raynos Points 82706

Vous n'avez pas à open le fichier, mais au lieu de cela, vous devez créer une ReadStream .

fs.createReadStream

Passez ensuite ce flux à Lazy

0 votes

Vous êtes une star ! Merci Raynos :) parfait.

2 votes

Existe-t-il une sorte d'événement de fin pour Lazy, lorsque toutes les lignes ont été lues ?

1 votes

@Max, essaie : new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })

43voto

polaretto Points 163

Il existe un très bon module pour lire un fichier ligne par ligne, il s'appelle lecteur de ligne

avec elle, vous écrivez tout 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 type "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);
    });
  }
});

4 votes

Cela fonctionne bien. Il lit même la dernière ligne ( !). Il est intéressant de noter qu'il conserve les \r s'il s'agit d'un fichier texte de type Windows. line.trim() permet de supprimer les éléments superflus. \r.

0 votes

Il est sous-optimal dans la mesure où l'entrée ne peut provenir que d'un fichier nommé, et non (pour un exemple évident et extrêmement important, process/stdin ). Du moins, si c'est le cas, ce n'est certainement pas évident en lisant le code et en l'essayant.

2 votes

En attendant, il existe un moyen intégré de lire les lignes d'un fichier, en utilisant la fonction readline module de base .

18voto

Ernelli Points 2133

Vous pouvez toujours utiliser votre propre lecteur de ligne. Je n'ai pas encore testé ce snippet, mais il divise correctement le flux entrant de morceaux en lignes sans le ' de fin de ligne'. \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 eu cette idée en travaillant sur un script d'analyse rapide des journaux qui avait besoin d'accumuler des données pendant l'analyse des journaux et j'ai pensé que ce 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 dépendre de modules tiers. Après avoir lu toutes les réponses à cette question, chacune utilisant divers modules pour gérer l'analyse syntaxique des lignes, une solution nodejs native à 13 SLOC pourrait être intéressante.

0 votes

Il ne semble pas y avoir de moyen trivial d'étendre cette fonction pour travailler avec des fichiers arbitraires, en dehors de simplement stdin ... à moins que je ne rate quelque chose.

3 votes

@hippietrail vous pouvez créer une ReadStream avec fs.createReadStream('./myBigFile.csv') et l'utiliser à la place de stdin

2 votes

Est-il garanti que chaque morceau ne contient que des lignes complètes ? Est-il garanti que les caractères UTF-8 à octets multiples ne sont pas divisés aux limites des blocs ?

12voto

Touv Points 715

Avec le module de support :

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});

0 votes

Joli. Cela fonctionne aussi pour n'importe quel fichier d'entrée : var inStream = fs.createReadStream('input.txt', {flags:'r'}); Mais votre syntaxe est plus propre que la méthode documentée qui consiste à utiliser .on() : carrier.carry(inStream).on('line', function(line) { ...

0 votes

Le transporteur ne semble gérer que \r\n et \n les fins de ligne. Si vous avez un jour besoin de traiter des fichiers de test de style MacOS antérieurs à OS X, ils utilisaient les méthodes suivantes \r et le transporteur ne s'en occupe pas. Il est surprenant de constater que de tels fichiers circulent encore dans la nature. Vous devrez peut-être aussi gérer explicitement le BOM (byte order mark) d'Unicode, qui est utilisé au début des fichiers texte dans la sphère d'influence de MS Windows.

0 votes

En attendant, il existe un moyen intégré de lire les lignes d'un fichier, en utilisant la fonction readline module de base .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X