node.js: legge un file di testo in un array. (Ogni riga di un elemento nella matrice.)


165

Vorrei leggere un file molto, molto grande in un array JavaScript in node.js.

Quindi, se il file è così:

first line
two 
three
...
...

Avrei l'array:

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

La funzione sarebbe simile a questa:

var array = load(filename); 

Pertanto l'idea di caricare tutto come una stringa e quindi dividerlo non è accettabile.


Questa domanda richiede alcune modifiche e operazioni di pulizia. Dice di leggere un file di testo in un array , ma quando leggi tutte le risposte e i commenti, significa davvero leggere un file di testo una riga alla volta . Per questa domanda @zswang ha la risposta migliore finora.
Jess,

Sì, basta leggere quel file e inserire ciascuna riga in un array: stackoverflow.com/a/34033928/1536309
Blair Anderson,

Risposte:


89

Se riesci ad adattare i dati finali a un array, non saresti in grado di adattarli a una stringa e dividerli, come è stato suggerito? In ogni caso, se desideri elaborare il file una riga alla volta, puoi anche provare qualcosa del genere:

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: (in risposta al commento di phopkins ) Penso che (almeno nelle versioni più recenti) la sottostringa non copi i dati ma crei un oggetto SlicedString speciale (da una rapida occhiata al codice sorgente v8). In ogni caso, ecco una modifica che evita la sottostringa menzionata (testata su un file di diversi megabyte del valore di "Tutto il lavoro e niente gioco rende Jack un ragazzo noioso"):

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

Grazie. per rispondere alla tua domanda: no, la stringa sarebbe troppo grande.
Chacko,

7
Ho provato questo su file di circa 2 MB o giù di lì ed è stato dolorosamente lento, molto più lento della lettura dei file in modo sincrono su una stringa. Penso che il problema sia la linea rimanente = rimanente. I "dati" del nodo potrebbero darti molto alla volta, e fare quella copia per ogni riga diventa rapidamente O (n ^ 2).
Fiona Hopkins,

La risposta di
@Finbar

444

Sincrono:

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

asincrono:

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
Grazie. Purtroppo ho dovuto modificare la mia domanda. Intendo come leggere un file di grandi dimensioni. Leggere tutto in una stringa non è accettabile.
Chacko,

1
Proprio quello di cui avevo bisogno. Semplice e veloce.
Hcabnettek,

16
Ho scoperto che facendo questo su un file creato da Windows, ho dovuto dividere \ r \ n ma quello ha rotto i Mac; quindi un più robusto; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); ha lavorato per entrambi
Will Hancock,

6
+1 C'è qualche problema in StackOverflow. Ora, trovo spesso risposte molto votate dopo aver fatto scorrere verso il basso di troppo. Questo è anche un esempio di questo. Ha il voto più alto ma è posizionato nella parte inferiore della pagina, per ultimo. Penso che Stackoverflow debba migliorare il loro algoritmo di ordinazione.
Shashwat,

1
@shashwat La persona che pone la domanda decide qual è la risposta giusta. In questo caso, avevano bisogno di una soluzione di streaming per file di grandi dimensioni e l'inserimento dell'intero file in una stringa è inaccettabile. Niente di sbagliato in SO, davvero.
legalizzare il

73

Utilizzando il Node.js modulo 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
Purtroppo c'è un problema con questa soluzione: non ottieni l'ultima riga se il file non ha un \nalla fine! Vedi: stackoverflow.com/questions/18450197/…
Yves M.

8
Nodo ha fissato tale questione con la \ 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
Per evitare che quanto sopra venga lanciato TypeError: fs.readFileSync(...).split is not a function, dovresti usare .toString () in questo modo:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285

11

usa readline ( documentazione ). ecco un esempio leggendo un file css, analizzando le icone e scrivendole su 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.linescon pacchetto JFile

Pseudo

var JFile=require('jfile');

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

Non dimenticare prima:

npm install jfile --save

5

Con un BufferedReader , ma la funzione dovrebbe essere asincrona:

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

voglio solo aggiungere @finbarr ottima risposta, una piccola correzione nell'esempio asincrono:

asincrono:

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 () è ciò che rilascia l'asincrono. chiamata.


3

Questa è una variazione sulla risposta sopra di @mtomis.

Crea un flusso di linee. Emette eventi 'data' e 'end', permettendoti di gestire la fine del flusso.

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;

Usalo come un wrapper:

var lineInput = new LineStream(input);

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

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

1
Si finirà con la condivisione di eventi tra istanze. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP

Di quali casi stai parlando esattamente?
oferei

1
prova a creare, var li1 = new LineStream(input1), li2 = new LineStream(input2);poi conta quante volte "end" viene
attivato

provato. 'end' è stato licenziato una volta per ogni istanza. 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'); }); output: ogni riga nel file di testo è stata attivata una volta per ogni istanza. così è stata la "fine".
oferei il

2

Ho avuto lo stesso problema e l'ho risolto riga per riga con il modulo

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

Almeno per me funziona come un incantesimo, sia in modalità sincrona che asincrona.

Inoltre, il problema con le righe che terminano non terminano \ n può essere risolto con l'opzione:

{ encoding: 'utf8', skipEmptyLines: false }

Elaborazione sincrona delle linee:

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'uso di Node.js v8 o versione successiva ha una nuova funzionalità che converte la normale funzione in una funzione asincrona.

util.promisify

È una funzione fantastica. Ecco l'esempio dell'analisi di 10000 numeri dal file txt in un array, contando le inversioni usando l'ordinamento di unione sui numeri.

// 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

Per leggere un grosso file nell'array puoi leggere riga per riga o pezzo per pezzo.

riga per riga fare riferimento alla mia risposta qui

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

pezzo per pezzo fare riferimento a questo articolo

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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.