node.js: lit un fichier texte dans un tableau. (Chaque ligne est un élément du tableau.)


165

Je voudrais lire un très, très gros fichier dans un tableau JavaScript dans node.js.

Donc, si le fichier est comme ça:

first line
two 
three
...
...

J'aurais le tableau:

['first line','two','three', ... , ... ] 

La fonction ressemblerait à ceci:

var array = load(filename); 

Par conséquent, l'idée de tout charger sous forme de chaîne puis de la diviser n'est pas acceptable.


Cette question nécessite une édition et un nettoyage sérieux. Il dit lire un fichier texte dans un tableau , mais lorsque vous lisez toutes les réponses et tous les commentaires, cela signifie vraiment lire un fichier texte une ligne à la fois . Pour cette question, @zswang a la meilleure réponse à ce jour.
Jess

yup vient de lire ce fichier et de pousser chaque ligne dans un tableau: stackoverflow.com/a/34033928/1536309
Blair Anderson

Réponses:


89

Si vous pouvez insérer les données finales dans un tableau, ne seriez-vous pas également en mesure de les insérer dans une chaîne et de les diviser, comme cela a été suggéré? Dans tous les cas, si vous souhaitez traiter le fichier ligne par ligne, vous pouvez également essayer quelque chose comme ceci:

var fs = require('fs');

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    while (index > -1) {
      var line = remaining.substring(0, index);
      remaining = remaining.substring(index + 1);
      func(line);
      index = remaining.indexOf('\n');
    }
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

function func(data) {
  console.log('Line: ' + data);
}

var input = fs.createReadStream('lines.txt');
readLines(input, func);

EDIT: (en réponse au commentaire de phopkins ) Je pense (au moins dans les versions plus récentes) que la sous-chaîne ne copie pas les données mais crée un objet SlicedString spécial (à partir d'un rapide coup d'œil sur le code source de la v8). En tout cas voici une modification qui évite la sous-chaîne mentionnée (testée sur un fichier de plusieurs mégaoctets de "Tout travail et aucun jeu fait de Jack un garçon ennuyeux"):

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    var last  = 0;
    while (index > -1) {
      var line = remaining.substring(last, index);
      last = index + 1;
      func(line);
      index = remaining.indexOf('\n', last);
    }

    remaining = remaining.substring(last);
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

Merci. pour répondre à votre question: non, la chaîne serait trop grande.
chacko du

7
J'ai essayé cela sur des fichiers d'environ 2 Mo environ et c'était extrêmement lent, beaucoup plus lent que la lecture synchrone des fichiers sur une chaîne. Je pense que le problème est la ligne restante = restant.substring. Les "données" de Node peuvent vous en donner beaucoup à la fois, et faire cette copie pour chaque ligne devient rapidement O (n ^ 2).
Fiona Hopkins

La réponse de @ Finbar est bien meilleure
rü-

444

Synchrone:

var fs = require('fs');
var array = fs.readFileSync('file.txt').toString().split("\n");
for(i in array) {
    console.log(array[i]);
}

Asynchrone:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
});

11
Merci. Malheureusement, j'ai dû modifier ma question. Je veux dire comment lire un fichier extrêmement volumineux. Tout lire dans une chaîne n'est pas acceptable.
chacko du

1
Juste ce dont j'avais besoin. Simple et rapide.
Hcabnettek

16
J'ai trouvé cela sur un fichier créé par Windows, j'ai dû diviser \ r \ n mais cela a cassé les Mac; donc un plus robuste; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); travaillé pour les deux
Will Hancock

6
+1 Il y a un problème dans Stackoverflow. Maintenant, je trouve souvent des réponses hautement votées après avoir fait défiler de trop loin. Ceci en est également un exemple. Il a le vote le plus élevé mais positionné en bas de page, tout en dernier. Je pense que Stackoverflow doit améliorer son algorithme de commande.
shashwat

1
@shashwat La personne qui pose la question décide quelle est la bonne réponse. Dans ce cas, ils avaient besoin d'une solution de streaming pour les fichiers volumineux et mettre le fichier entier dans une chaîne est inacceptable. Rien de mal avec SO, vraiment.
légaliser le

73

En utilisant le Node.js module readline .

var fs = require('fs');
var readline = require('readline');

var filename = process.argv[2];
readline.createInterface({
    input: fs.createReadStream(filename),
    terminal: false
}).on('line', function(line) {
   console.log('Line: ' + line);
});

1
Malheureusement, il y a un problème avec cette solution: vous n'obtenez pas la dernière ligne si le fichier n'a pas\n à la fin! Voir: stackoverflow.com/questions/18450197/…
Yves M.

8
Node a corrigé ce problème avec le \ n stackoverflow.com/a/32599033/3763850
Gemtastic

14

js:

var array = fs.readFileSync('file.txt', 'utf8').split('\n');

ts:

var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');

1
Pour éviter que ce qui précède ne se lance TypeError: fs.readFileSync(...).split is not a function, vous devez utiliser .toString () comme ceci:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285

11

utilisez readline ( documentation ). voici un exemple de lecture d'un fichier css, d'analyse des icônes et de leur écriture dans json

var results = [];
  var rl = require('readline').createInterface({
    input: require('fs').createReadStream('./assets/stylesheets/_icons.scss')
  });


  // for every new line, if it matches the regex, add it to an array
  // this is ugly regex :)
  rl.on('line', function (line) {
    var re = /\.icon-icon.*:/;
    var match;
    if ((match = re.exec(line)) !== null) {
      results.push(match[0].replace(".",'').replace(":",''));
    }
  });


  // readline emits a close event when the file is read.
  rl.on('close', function(){
    var outputFilename = './icons.json';
    fs.writeFile(outputFilename, JSON.stringify(results, null, 2), function(err) {
        if(err) {
          console.log(err);
        } else {
          console.log("JSON saved to " + outputFilename);
        }
    });
  });

6

file.linesavec le package JFile

Pseudo

var JFile=require('jfile');

var myF=new JFile("./data.txt");
myF.lines // ["first line","second line"] ....

N'oubliez pas avant:

npm install jfile --save

5

Avec un BufferedReader , mais la fonction doit être asynchrone:

var load = function (file, cb){
    var lines = [];
    new BufferedReader (file, { encoding: "utf8" })
        .on ("error", function (error){
            cb (error, null);
        })
        .on ("line", function (line){
            lines.push (line);
        })
        .on ("end", function (){
            cb (null, lines);
        })
        .read ();
};

load ("file", function (error, lines){
    if (error) return console.log (error);
    console.log (lines);
});

4

je veux juste ajouter une excellente réponse @finbarr, une petite correction dans l'exemple asynchrone:

Asynchrone:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
    done();
});

@MadPhysicist, done () est ce qui libère l'async. appel.


3

Ceci est une variation de la réponse ci-dessus par @mtomis.

Cela crée un flot de lignes. Il émet des événements «data» et «end», vous permettant de gérer la fin du flux.

var events = require('events');

var LineStream = function (input) {
    var remaining = '';

    input.on('data', function (data) {
        remaining += data;
        var index = remaining.indexOf('\n');
        var last = 0;
        while (index > -1) {
            var line = remaining.substring(last, index);
            last = index + 1;
            this.emit('data', line);
            index = remaining.indexOf('\n', last);
        }
        remaining = remaining.substring(last);
    }.bind(this));

    input.on('end', function() {
        if (remaining.length > 0) {
            this.emit('data', remaining);
        }
        this.emit('end');
    }.bind(this));
}

LineStream.prototype = new events.EventEmitter;

Utilisez-le comme emballage:

var lineInput = new LineStream(input);

lineInput.on('data', function (line) {
    // handle line
});

lineInput.on('end', function() {
    // wrap it up
});

1
Vous finirez par partager les événements entre les instances. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP

De quels cas parlez-vous exactement?
oferei

1
essayez de créer var li1 = new LineStream(input1), li2 = new LineStream(input2);puis comptez le nombre de déclenchements de «fin» pour chacun d'entre eux
CTAPbIu_MABP

Essayé. «end» a été déclenché une fois pour chaque instance. var fs = require('fs'); var input1 = fs.createReadStream('text.txt'); var ls1 = new LineStream(input1); ls1.on('data', function (line) { console.log('1:line=' + line); }); ls1.on('end', function (line) { console.log('1:fin'); }); var input2 = fs.createReadStream('text.txt'); var ls2 = new LineStream(input2); ls2.on('data', function (line) { console.log('2:line=' + line); }); ls2.on('end', function (line) { console.log('2:fin'); }); sortie: chaque ligne du fichier texte a été déclenchée une fois pour chaque instance. était donc «fin».
oferei

2

J'ai eu le même problème, et je l'ai résolu avec le module ligne par ligne

https://www.npmjs.com/package/line-by-line

Au moins pour moi fonctionne comme un charme, à la fois en mode synchrone et asynchrone.

De plus, le problème avec les lignes se terminant et ne se terminant pas \ n peut être résolu avec l'option:

{ encoding: 'utf8', skipEmptyLines: false }

Traitement synchrone des lignes:

var LineByLineReader = require('line-by-line'),
    lr = new LineByLineReader('big_file.txt');

lr.on('error', function (err) {
    // 'err' contains error object
});

lr.on('line', function (line) {
    // 'line' contains the current line without the trailing newline character.
});

lr.on('end', function () {
    // All lines are read, file is closed now.
}); 

2

L'utilisation de Node.js v8 ou version ultérieure a une nouvelle fonctionnalité qui convertit une fonction normale en une fonction asynchrone.

util.promisify

C'est une fonctionnalité géniale. Voici l'exemple d'analyse de 10000 nombres du fichier txt dans un tableau, comptant les inversions à l'aide du tri par fusion sur les nombres.

// read from txt file
const util = require('util');
const fs = require('fs')
fs.readFileAsync = util.promisify(fs.readFile);
let result = []

const parseTxt = async (csvFile) => {
  let fields, obj
  const data = await fs.readFileAsync(csvFile)
  const str = data.toString()
  const lines = str.split('\r\n')
  // const lines = str
  console.log("lines", lines)
  // console.log("str", str)

  lines.map(line => {
    if(!line) {return null}
    result.push(Number(line))
  })
  console.log("result",result)
  return result
}
parseTxt('./count-inversion.txt').then(() => {
  console.log(mergeSort({arr: result, count: 0}))
})

1

Pour lire un gros fichier dans un tableau, vous pouvez lire ligne par ligne ou morceau par morceau.

ligne par ligne référez-vous à ma réponse ici

var fs = require('fs'),
    es = require('event-stream'),

var lines = [];

var s = fs.createReadStream('filepath')
    .pipe(es.split())
    .pipe(es.mapSync(function(line) {
        //pause the readstream
        s.pause();
        lines.push(line);
        s.resume();
    })
    .on('error', function(err) {
        console.log('Error:', err);
    })
    .on('end', function() {
        console.log('Finish reading.');
        console.log(lines);
    })
);

morceau par morceau se référer à cet article

var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
    offset += bytesRead;
    var str = chunkBuffer.slice(0, bytesRead).toString();
    var arr = str.split('\n');

    if(bytesRead = chunkSize) {
        // the last item of the arr may be not a full line, leave it to the next chunk
        offset -= arr.pop().length;
    }
    lines.push(arr);
}
console.log(lines);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.