node.js: membaca file teks ke dalam array. (Setiap baris item dalam array.)

163

Saya ingin membaca file yang sangat, sangat besar ke dalam array JavaScript di node.js.

Jadi, jika file seperti ini:

first line
two 
three
...
...

Saya akan memiliki array:

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

Fungsi akan terlihat seperti ini:

var array = load(filename); 

Oleh karena itu gagasan memuat semuanya sebagai string dan kemudian membaginya tidak dapat diterima.

chacko
sumber
Pertanyaan ini perlu diedit dan dibersihkan dengan serius. Dikatakan membaca file teks ke dalam array , tetapi ketika Anda membaca semua jawaban dan komentar, itu benar-benar berarti membaca file teks satu baris pada suatu waktu . Untuk pertanyaan itu @zswang memiliki jawaban terbaik sejauh ini.
Jess
yup baru saja membaca file itu dan mendorong setiap baris ke dalam array: stackoverflow.com/a/34033928/1536309
Blair Anderson

Jawaban:

89

Jika Anda bisa memasukkan data akhir ke dalam sebuah array maka bukankah Anda juga bisa memasukkannya ke dalam sebuah string dan membaginya, seperti yang telah disarankan? Dalam hal apa pun jika Anda ingin memproses file satu baris pada satu waktu Anda juga dapat mencoba sesuatu seperti ini:

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: (dalam menanggapi komentar oleh phopkins ) Saya pikir (setidaknya dalam versi yang lebih baru) substring tidak menyalin data tetapi membuat objek SlicedString khusus (dari pandangan sekilas pada kode sumber v8). Dalam hal apa pun di sini adalah modifikasi yang menghindari substring yang disebutkan (diuji pada file beberapa megabyte senilai "Semua pekerjaan dan tidak ada permainan membuat Jack menjadi anak yang membosankan"):

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
sumber
Terima kasih. untuk menjawab pertanyaan Anda: tidak, string akan terlalu besar.
chacko
7
Saya mencoba ini pada file sekitar 2MB atau lebih dan itu sangat lambat, jauh lebih lambat daripada membaca file secara sinkron ke string. Saya pikir masalahnya adalah baris yang tersisa = tersisa.substring. "Data" Node mungkin memberi Anda banyak waktu, dan melakukan salinan itu untuk setiap baris dengan cepat menjadi O (n ^ 2).
Fiona Hopkins
@ Finbar jawaban jauh lebih baik
rü-
442

Sinkronis:

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

Asinkron:

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
sumber
11
Terima kasih. Sayangnya saya harus mengedit pertanyaan saya. Maksud saya cara membaca file besar secara besar-besaran. Membaca semuanya dalam sebuah string tidak dapat diterima.
chacko
1
Apa yang saya butuhkan. Sederhana dan cepat.
Hcabnettek
16
Saya menemukan melakukan ini pada file yang dibuat oleh Windows, saya harus membagi \ r \ n tetapi itu memecah Mac; jadi lebih kuat; _array = string.replace (/ \ r \ n / g, '\ n'). split ('\ n'); bekerja untuk keduanya
Will Hancock
6
+1 Ada beberapa masalah di Stackoverflow. Sekarang, saya sering menemukan jawaban yang sangat banyak dipilih setelah bergulir terlalu jauh. Ini juga contohnya. Ini memiliki pemungutan suara tertinggi tetapi diposisikan di bagian bawah halaman, sangat terakhir. Saya pikir Stackoverflow perlu meningkatkan algoritma pemesanan mereka.
shashwat
1
@shashwat Orang yang mengajukan pertanyaan harus memutuskan mana jawaban yang tepat. Dalam hal ini, mereka membutuhkan solusi streaming untuk file besar dan meletakkan seluruh file dalam sebuah string tidak dapat diterima. Tidak ada yang salah dengan SO, sungguh.
mengesahkan
73

Menggunakan Node.js modul 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
sumber
1
Sayangnya ada masalah dengan solusi ini: Anda tidak mendapatkan baris terakhir jika file tidak memiliki \ndi akhir! Lihat: stackoverflow.com/questions/18450197/…
Yves M.
8
Node telah memperbaiki masalah itu dengan \ 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
sumber
1
Untuk mencegah lemparan di atas TypeError: fs.readFileSync(...).split is not a function, Anda harus menggunakan .toString () seperti ini:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285
11

gunakan readline ( dokumentasi ). inilah contoh membaca file css, parsing untuk ikon dan menulisnya ke 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
sumber
6

file.linesdengan paket JFile

Semu

var JFile=require('jfile');

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

Jangan lupa sebelumnya:

npm install jfile --save
Abdennour TOUMI
sumber
5

Dengan BufferedReader , tetapi fungsinya harus asinkron:

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
sumber
4

Saya hanya ingin menambahkan jawaban bagus @finbarr, sedikit perbaikan pada contoh asinkron:

Asinkron:

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 () adalah apa yang melepaskan async. panggilan.

HernanFila
sumber
3

Ini adalah variasi pada jawaban di atas oleh @mtomis.

Itu menciptakan aliran garis. Itu memancarkan 'data' dan 'mengakhiri' acara, memungkinkan Anda untuk menangani akhir aliran.

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;

Gunakan sebagai pembungkus:

var lineInput = new LineStream(input);

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

lineInput.on('end', function() {
    // wrap it up
});
oferei
sumber
1
Anda akan berakhir dengan memiliki acara yang dibagikan di antara instance. var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP
Contoh apa yang Anda bicarakan sebenarnya?
oferei
1
coba buat var li1 = new LineStream(input1), li2 = new LineStream(input2);lalu hitung berapa kali 'ujung' dipecat untuk masing-masing
CTAPbIu_MABP
mencobanya. 'end' dipecat satu kali untuk setiap 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'); }); output: setiap baris dalam file teks dipecat satu kali untuk setiap instance. begitu juga 'akhir'.
oferei
2

Saya memiliki masalah yang sama, dan saya telah menyelesaikannya dengan modul line-by-line

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

Setidaknya bagi saya berfungsi seperti pesona, baik dalam mode sinkron maupun asinkron.

Juga, masalah dengan penghentian baris tidak berhenti \ n dapat diselesaikan dengan opsi:

{ encoding: 'utf8', skipEmptyLines: false }

Pemrosesan garis sinkron:

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
sumber
2

Menggunakan Node.js v8 atau yang lebih baru memiliki fitur baru yang mengubah fungsi normal menjadi fungsi async.

util.promisify

Ini fitur yang luar biasa. Berikut adalah contoh parsing 10.000 angka dari file txt ke dalam array, menghitung inversi menggunakan penggabungan gabung pada angka.

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

Untuk membaca file besar ke dalam array Anda dapat membaca baris demi baris atau potongan demi potongan.

baris demi baris merujuk pada jawaban saya di sini

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

chunk by chunk merujuk ke artikel ini

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
sumber