node.js: wczytaj plik tekstowy do tablicy. (Każdy wiersz jest elementem tablicy).

164

Chciałbym wczytać bardzo, bardzo duży plik do tablicy JavaScript w node.js.

Więc jeśli plik wygląda tak:

first line
two 
three
...
...

Miałbym tablicę:

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

Funkcja wyglądałaby tak:

var array = load(filename); 

Dlatego pomysł załadowania tego wszystkiego jako łańcucha, a następnie podzielenia go, jest nie do przyjęcia.

chacko
źródło
To pytanie wymaga poważnej edycji i uporządkowania. Mówi, że czytaj plik tekstowy do tablicy , ale kiedy czytasz wszystkie odpowiedzi i komentarze, naprawdę oznacza to czytanie pliku tekstowego po jednym wierszu na raz . Na to pytanie @zswang ma dotychczas najlepszą odpowiedź.
Jess
tak, po prostu przeczytaj ten plik i wepchnij każdy wiersz do tablicy: stackoverflow.com/a/34033928/1536309
Blair Anderson

Odpowiedzi:

89

Jeśli możesz dopasować ostateczne dane do tablicy, to czy nie byłbyś również w stanie dopasować go do ciągu i podzielić go, jak zasugerowano? W każdym razie, jeśli chcesz przetwarzać plik po jednej linii na raz, możesz również spróbować czegoś takiego:

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

EDYCJA: (w odpowiedzi na komentarz phopkinsa ) Myślę (przynajmniej w nowszych wersjach) podciąg nie kopiuje danych, ale tworzy specjalny obiekt SlicedString (z szybkiego spojrzenia na kod źródłowy v8). W każdym razie tutaj jest modyfikacja, która unika wspomnianego podciągu (przetestowano na pliku o wartości kilku megabajtów „Cała praca i brak zabawy sprawia, że ​​Jack jest nudnym chłopcem”):

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);
    }
  });
}
mtomis
źródło
Dzięki. aby odpowiedzieć na twoje pytanie: nie, ciąg byłby za duży.
chacko
7
Próbowałem tego na plikach o wielkości około 2 MB i było to boleśnie powolne, znacznie wolniejsze niż wczytywanie plików synchronicznie do ciągu. Myślę, że problemem jest pozostała linia = pozostała podłańcuch. „Dane” węzła mogą dać ci wiele na raz, a wykonanie tej kopii dla każdej linii szybko staje się O (n ^ 2).
Fiona Hopkins
Odpowiedź @ Finbar jest znacznie lepsza
rü-
444

Synchroniczny:

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

Asynchroniczny:

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]);
    }
});
Finbarr
źródło
11
dzięki. Niestety musiałem edytować moje pytanie. Mam na myśli, jak czytać bardzo duży plik. Czytanie tego wszystkiego w łańcuchu jest niedopuszczalne.
chacko
1
Dokładnie to, czego potrzebowałem. Prosto i szybko.
Hcabnettek
16
Zauważyłem, że robię to na pliku utworzonym przez Windows, musiałem podzielić \ r \ n, ale to zepsuło komputery Mac; więc bardziej wytrzymały; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); pracował dla obu
Will Hancock
6
+1 Wystąpił problem w Stackoverflow. Teraz często znajduję wysoko ocenione odpowiedzi po przewinięciu zbyt daleko w dół. To też jest tego przykład. Ma najwięcej głosów, ale znajduje się na dole strony, na samym końcu. Myślę, że Stackoverflow musi poprawić algorytm porządkowania.
shashwat
1
@shashwat Osoba zadająca pytanie decyduje, która odpowiedź jest prawidłowa. W tym przypadku potrzebowali rozwiązania do przesyłania strumieniowego dużych plików, a umieszczanie całego pliku w ciągu jest niedopuszczalne. Naprawdę nic złego w SO.
zalegalizować
73

Korzystanie z node.js moduł 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);
});
zswang
źródło
1
Niestety jest problem z tym rozwiązaniem: nie otrzymasz ostatniej linii, jeśli plik nie ma \nna końcu! Zobacz: stackoverflow.com/questions/18450197/…
Yves M.
8
Węzeł naprawił ten problem z \ 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');
hojin
źródło
1
Aby zapobiec powyższemu rzucaniu TypeError: fs.readFileSync(...).split is not a function, powinieneś użyć .toString () w ten sposób:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285
11

użyj readline ( dokumentacja ). oto przykład czytania pliku css, analizowania ikon i zapisywania ich w 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);
        }
    });
  });
Blair Anderson
źródło
6

file.linesz pakietem JFile

Rzekomy

var JFile=require('jfile');

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

Nie zapomnij wcześniej:

npm install jfile --save
Abdennour TOUMI
źródło
5

Z BufferedReader , ale funkcja powinna być asynchroniczna:

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);
});
Gabriel Llamas
źródło
4

chcę tylko dodać @finbarr świetną odpowiedź, małą poprawkę w asynchronicznym przykładzie:

Asynchroniczny:

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 () jest tym, co zwalnia async. połączenie.

HernanFila
źródło
3

To jest wariacja na temat powyższej odpowiedzi autorstwa @mtomis.

Tworzy strumień linii. Emituje zdarzenia „data” i „end”, co pozwala obsłużyć koniec strumienia.

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;

Użyj go jako opakowania:

var lineInput = new LineStream(input);

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

lineInput.on('end', function() {
    // wrap it up
});
oferei
źródło
1
Zakończysz udostępnianiem wydarzeń pomiędzy instancjami. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP
O jakich dokładnie przypadkach mówisz?
oferei
1
spróbuj utworzyć, var li1 = new LineStream(input1), li2 = new LineStream(input2);a następnie policz, ile razy 'koniec' jest uruchamiany dla każdego z nich
CTAPbIu_MABP
spróbował tego. „koniec” został uruchomiony raz dla każdej instancji. 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'); }); dane wyjściowe: każda linia w pliku tekstowym została uruchomiona raz dla każdej instancji. więc był „koniec”.
oferei
2

Miałem ten sam problem i rozwiązałem go z modułem linia po linii

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

Przynajmniej dla mnie działa jak urok, zarówno w trybie synchronicznym, jak i asynchronicznym.

Również problem z liniami kończącymi się, ale nie kończącymi się \ n można rozwiązać za pomocą opcji:

{ encoding: 'utf8', skipEmptyLines: false }

Synchroniczna obróbka linii:

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.
}); 
Antoni
źródło
2

Korzystanie z Node.js w wersji 8 lub nowszej ma nową funkcję, która konwertuje normalną funkcję na funkcję asynchroniczną.

util.promisify

To niesamowita funkcja. Oto przykład parsowania 10000 liczb z pliku txt do tablicy, zliczając inwersje przy użyciu sortowania scalającego na liczbach.

// 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}))
})
Seunghun Sunmoon Lee
źródło
1

Aby wczytać duży plik do tablicy, możesz czytać wiersz po wierszu lub fragment po kawałku.

wiersz po wierszu zapoznaj się z moją odpowiedzią tutaj

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

fragment po kawałku odnoszą się do tego artykułu

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);
Kris Roofe
źródło