Membaca file satu per satu baris di node.js?

553

Saya mencoba membaca file besar satu per satu. Saya menemukan pertanyaan tentang Quora yang membahas masalah ini, tetapi saya kehilangan beberapa koneksi untuk membuat semuanya cocok.

 var Lazy=require("lazy");
 new Lazy(process.stdin)
     .lines
     .forEach(
          function(line) { 
              console.log(line.toString()); 
          }
 );
 process.stdin.resume();

Bagian yang ingin saya ketahui adalah bagaimana saya bisa membaca satu baris pada satu waktu dari sebuah file, bukan STDIN seperti dalam sampel ini.

Saya mencoba:

 fs.open('./VeryBigFile.csv', 'r', '0666', Process);

 function Process(err, fd) {
    if (err) throw err;
    // DO lazy read 
 }

tapi itu tidak berhasil. Saya tahu bahwa dalam keadaan darurat saya dapat kembali menggunakan sesuatu seperti PHP, tapi saya ingin mencari tahu.

Saya tidak berpikir jawaban lain akan bekerja karena file tersebut jauh lebih besar daripada server tempat saya menjalankannya memiliki memori.

Alex C
sumber
2
Ini ternyata cukup sulit hanya menggunakan level rendah fs.readSync(). Anda dapat membaca oktet biner ke dalam buffer tetapi tidak ada cara mudah untuk menangani sebagian karakter UTF-8 atau UTF-16 tanpa memeriksa buffer sebelum menerjemahkannya ke string JavaScript dan memindai EOL. The Buffer()tipe tidak memiliki sebagai set kaya fungsi untuk beroperasi pada contoh sebagai string asli, tapi string asli tidak dapat berisi data biner. Tampaknya bagi saya bahwa kekurangan cara untuk membaca baris teks dari file arbitrer yang sewenang-wenang adalah kesenjangan nyata dalam node.js.
hippietrail
5
Baris kosong yang dibaca dengan metode ini dapat dikonversi ke baris dengan 0 tunggal (kode karakter aktual untuk 0) di dalamnya. Saya harus meretas baris ini di sana:if (line.length==1 && line[0] == 48) special(line);
Thabo
2
Seseorang mungkin juga menggunakan paket 'line-by-line' yang melakukan pekerjaan dengan sempurna.
Patrice
1
Harap perbarui pertanyaan untuk mengatakan bahwa solusinya adalah menggunakan aliran transformasi
Gabriel Llamas
2
@DanDascalescu jika Anda suka, Anda dapat menambahkan ini ke daftar: contoh Anda mendarat sedikit dimodifikasi di nodeAPI docs github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

Jawaban:

790

Sejak Node.js v0.12 dan pada Node.js v4.0.0, ada modul inti readline yang stabil . Berikut cara termudah untuk membaca baris dari file, tanpa modul eksternal apa pun:

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

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

Atau sebagai alternatif:

var lineReader = require('readline').createInterface({
  input: require('fs').createReadStream('file.in')
});

lineReader.on('line', function (line) {
  console.log('Line from file:', line);
});

Baris terakhir dibaca dengan benar (pada Node v0.12 atau yang lebih baru), bahkan jika tidak ada final \n.

PEMBARUAN : contoh ini telah ditambahkan ke dokumentasi resmi API Node .

Dan Dascalescu
sumber
7
Anda memerlukan terminal: false dalam definisi
createInterface
64
Bagaimana cara menentukan baris terakhir? Dengan menangkap acara "tutup":rl.on('close', cb)
Hijau
27
Readline adalah untuk tujuan yang sama dengan GNU Readline , bukan untuk membaca file baris demi baris. Ada beberapa peringatan dalam menggunakannya untuk membaca file dan ini bukan praktik terbaik.
Nakedible
8
@Nakedible: menarik. Bisakah Anda memposting jawaban dengan metode yang lebih baik?
Dan Dascalescu
6
Saya menganggap github.com/jahewson/node-byline sebagai implementasi terbaik dari pembacaan baris demi baris, tetapi pendapat dapat bervariasi.
Nakedible
165

Untuk operasi sederhana seperti itu seharusnya tidak ada ketergantungan pada modul pihak ketiga. Mudah saja.

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);
});
kofrasa
sumber
33
sayangnya, solusi menarik ini tidak berfungsi dengan benar — lineperistiwa terjadi hanya setelah memukul \n, yaitu, semua alternatif terlewatkan (lihat unicode.org/reports/tr18/#Line_Boundaries ). # 2, data setelah yang terakhir \ndiabaikan secara diam-diam (lihat stackoverflow.com/questions/18450197/… ). Saya akan menyebut solusi ini berbahaya karena berfungsi untuk 99% dari semua file dan untuk 99% dari data tetapi gagal diam - diam untuk sisanya. setiap kali Anda melakukannya, fs.writeFileSync( path, lines.join('\n'))Anda telah menulis file yang hanya akan dibaca sebagian oleh solusi di atas.
mengalir
4
Ada masalah dengan solusi ini. Jika Anda menggunakan your.js <lines.txt Anda tidak mendapatkan baris terakhir. Jika tidak memiliki '\ n' pada akhirnya tentu saja.
zag2art
The readlinepaket berperilaku dalam cara-cara yang benar-benar aneh untuk programmer Unix / Linux yang berpengalaman.
Runcing
11
rd.on("close", ..);dapat digunakan sebagai panggilan balik (terjadi ketika semua baris dibaca)
Luca Steeb
6
Masalah "data setelah \ n" terakhir tampaknya diselesaikan dalam versi simpul saya (0.12.7). Jadi saya lebih suka jawaban ini, yang tampaknya paling sederhana dan paling elegan.
Myk Melez
63

Anda tidak harus ke openfile, tetapi Anda harus membuat ReadStream.

fs.createReadStream

Kemudian berikan aliran itu ke Lazy

Raynos
sumber
2
Apakah ada sesuatu seperti acara akhir untuk Malas? Kapan semua baris sudah dibaca?
Maks.
1
@ Max, Coba:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi
6
@Cecchi dan @Max, jangan gunakan join karena akan buffer seluruh file dalam memori. Sebagai gantinya, cukup dengarkan acara 'akhir':new lazy(...).lines.forEach(...).on('end', function() {...})
Corin
3
@Cecchi, @Corin, dan @ Max: Untuk apa nilainya, saya membuat rantai gila .on('end'... setelahnya .forEach(...) , padahal sebenarnya semua berperilaku seperti yang diharapkan ketika saya mengikat acara terlebih dahulu .
crowjonah
52
Hasil ini sangat tinggi pada hasil pencarian, sehingga perlu dicatat bahwa Lazy terlihat ditinggalkan. Sudah 7 bulan tanpa perubahan, dan memiliki beberapa bug yang mengerikan (baris terakhir diabaikan, kebocoran memori besar, dll).
blu
38

ada modul yang sangat bagus untuk membaca file baris demi baris, itu disebut line-reader

dengan itu Anda cukup menulis:

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

Anda bahkan dapat mengulangi file dengan antarmuka "java-style", jika Anda memerlukan lebih banyak kontrol:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});
polaretto
sumber
4
Ini bekerja dengan baik. Bahkan membaca baris terakhir (!). Perlu disebutkan bahwa itu membuat file jika itu adalah file teks gaya windows. line.trim () melakukan trik menghapus ekstra.
Pierre-Luc Bertrand
Sub-optimal dalam input itu hanya bisa dari file bernama, dan tidak (untuk contoh yang jelas dan sangat penting, process/stdin). Setidaknya, jika bisa, tentu tidak jelas dari membaca kode dan mencobanya.
Runcing
2
Sementara itu ada cara built-in untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
Ini sudah tua, tetapi siapa tahu ada yang menemukannya: function(reader)dan function(line)seharusnya: function(err,reader)dan function(err,line).
jallmer
1
Sekadar catatan, line-readerbacalah file secara tidak sinkron. Alternatif sinkron untuk itu adalahline-reader-sync
Prajwal Dhatwalia
31
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})
John Williams
sumber
42
Ini akan membaca seluruh file dalam memori, kemudian membaginya menjadi beberapa baris. Bukan itu pertanyaannya. Intinya adalah untuk dapat membaca file besar secara berurutan, sesuai permintaan.
Dan Dascalescu
2
Ini sesuai dengan kasus penggunaan saya, saya sedang mencari cara sederhana untuk mengubah input dari satu skrip ke format lain. Terima kasih!
Callat
23

Pembaruan pada tahun 2019

Contoh yang luar biasa sudah diposting pada dokumentasi resmi Nodejs. sini

Ini mengharuskan Nodejs terbaru diinstal pada mesin Anda. > 11.4

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

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();
Pengembang Pimpinan
sumber
jawaban ini jauh lebih baik daripada apa pun di atas berkat perilaku berbasis janji, yang secara khusus menunjukkan EOF.
phil294
Terima kasih, itu manis.
Goran Stoyanov
3
Mungkin ini jelas bagi orang lain, tetapi perlu beberapa saat untuk debug: jika Anda memiliki awaitantara createInterface()panggilan dan awal for awaitloop, Anda akan secara misterius kehilangan garis dari awal file. createInterface()segera mulai memancarkan garis di belakang layar, dan iterator async yang secara implisit dibuat dengan const line of rltidak dapat mulai mendengarkan garis-garis itu sampai dibuat.
andrewdotn
19

Topik lama, tetapi ini berfungsi:

var rl = readline.createInterface({
      input : fs.createReadStream('/path/file.txt'),
      output: process.stdout,
      terminal: false
})
rl.on('line',function(line){
     console.log(line) //or parse line
})

Sederhana. Tidak perlu modul eksternal.

nf071590
sumber
2
Jika Anda mendapatkan readline is not definedatau fs is not defined, menambah var readline = require('readline');dan membuatnya var fs = require('fs');berfungsi. Kalau tidak manis, kode manis. Terima kasih.
bergie3000
12
Jawaban ini adalah duplikat yang tepat dari jawaban sebelumnya , tetapi tanpa komentar yang memperingatkan paket readline ditandai tidak stabil (masih belum stabil pada April 2015) dan, pada pertengahan 2013, mengalami kesulitan membaca baris terakhir file tanpa akhiran garis . Masalah baris terakhir muncul saat pertama kali saya menggunakannya di v0.10.35, & kemudian hilang. / argh
ruffin
Anda tidak perlu menentukan output jika semua yang Anda lakukan adalah membaca dari aliran file .
Dan Dascalescu
18

Anda selalu dapat memutar pembaca baris Anda sendiri. Saya belum membuat tolok cuplikan ini, tetapi ia dengan benar membagi aliran potongan yang masuk ke dalam garis tanpa mengekor '\ 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();

Saya memang datang dengan ini ketika bekerja pada skrip parsing log cepat yang diperlukan untuk mengumpulkan data selama log parsing dan saya merasa bahwa akan lebih baik untuk mencoba melakukan ini menggunakan js dan node daripada menggunakan perl atau bash.

Bagaimanapun, saya merasa bahwa skrip nodejs kecil harus mandiri dan tidak bergantung pada modul pihak ketiga sehingga setelah membaca semua jawaban untuk pertanyaan ini, masing-masing menggunakan berbagai modul untuk menangani penguraian garis, solusi nodejs asli 13 SLOC mungkin menarik.

Ernelli
sumber
Sepertinya tidak ada cara sepele untuk memperluas ini untuk bekerja dengan file sewenang-wenang selain hanya stdin... kecuali saya melewatkan sesuatu.
hippietrail
3
@hippietrail Anda dapat membuat ReadStreamdengan fs.createReadStream('./myBigFile.csv')dan menggunakannya sebagai gantistdin
nolith
2
Apakah setiap potongan dijamin hanya berisi baris lengkap? Apakah multi-byte karakter UTF-8 dijamin tidak akan terpecah pada batas chunk?
hippietrail
1
@hippietrail Saya tidak berpikir bahwa karakter multibyte ditangani dengan benar oleh implementasi ini. Untuk itu, kita harus terlebih dahulu mengkonversi buffer dengan string dan melacak karakter yang dibagi antara dua buffer. Untuk melakukan itu dengan benar, seseorang dapat menggunakan built in StringDecoder
Ernelli
Sementara itu ada cara built-in untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
12

Dengan modul operator :

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});
Touv
sumber
Bagus. Ini juga berfungsi untuk file input apa pun: var inStream = fs.createReadStream('input.txt', {flags:'r'}); Tetapi sintaks Anda lebih bersih daripada metode yang terdokumentasi dalam menggunakan .on ():carrier.carry(inStream).on('line', function(line) { ...
Brent Faust
pembawa tampaknya hanya menangani \r\ndan \nujung garis. Jika Anda perlu berurusan dengan file uji gaya MacOS dari sebelum OS X, mereka menggunakan \rdan operator tidak menangani ini. Anehnya, masih ada file-file seperti mengambang di alam liar. Anda mungkin juga perlu menangani Unicode BOM (tanda urutan byte) secara eksplisit, ini digunakan pada awal file teks dalam lingkup pengaruh MS Windows.
hippietrail
Sementara itu ada cara built-in untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
9

Saya berakhir dengan kebocoran memori besar-besaran menggunakan Malas untuk membaca baris demi baris ketika mencoba untuk memproses baris-baris itu dan menulisnya ke aliran lain karena cara menguras / jeda / melanjutkan dalam karya simpul (lihat: http: // elegantcode .com / 2011/04/06 / mengambil-baby-steps-with-node-js-pumping-data-between-stream / (saya suka orang ini btw)). Saya belum melihat dari dekat pada Malas untuk mengerti persis mengapa, tetapi saya tidak bisa menghentikan aliran baca saya untuk memungkinkan pengeringan tanpa Malas keluar.

Saya menulis kode untuk memproses file csv besar menjadi dokumen xml, Anda dapat melihat kode di sini: https://github.com/j03m/node-csv2xml

Jika Anda menjalankan revisi sebelumnya dengan baris Malas bocor. Revisi terbaru tidak bocor sama sekali dan Anda mungkin dapat menggunakannya sebagai dasar untuk pembaca / prosesor. Meskipun saya punya beberapa barang khusus di sana.

Sunting: Saya kira saya juga harus mencatat bahwa kode saya dengan Lazy berfungsi dengan baik sampai saya menemukan diri saya menulis fragmen xml cukup besar yang menguras / jeda / melanjutkan karena suatu keharusan. Untuk potongan yang lebih kecil itu baik-baik saja.

j03m
sumber
Sementara itu ada cara yang lebih sederhana untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
ya. Itu cara yang benar sekarang. Tapi ini dari 2011. :)
j03m
8

Edit:

Gunakan aliran transformasi .


Dengan BufferedReader Anda dapat membaca baris.

new BufferedReader ("lorem ipsum", { encoding: "utf8" })
    .on ("error", function (error){
        console.log ("error: " + error);
    })
    .on ("line", function (line){
        console.log ("line: " + line);
    })
    .on ("end", function (){
        console.log ("EOF");
    })
    .read ();
Gabriel Llamas
sumber
1
Sementara itu ada cara yang lebih sederhana untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
7

Sejak memposting jawaban asli saya, saya menemukan bahwa split adalah modul node yang sangat mudah digunakan untuk membaca baris dalam file; Yang juga menerima parameter opsional.

var split = require('split');
fs.createReadStream(file)
    .pipe(split())
    .on('data', function (line) {
      //each chunk now is a seperate line! 
    });

Belum diuji pada file yang sangat besar. Beri tahu kami jika Anda melakukannya.

nf071590
sumber
6

Saya frustrasi dengan kurangnya solusi yang komprehensif untuk ini, jadi saya mengumpulkan upaya saya sendiri ( git / npm ). Daftar fitur yang disalin:

  • Pemrosesan jalur interaktif (berbasis panggilan balik, tidak memuat seluruh file ke dalam RAM)
  • Secara opsional, kembalikan semua baris dalam array (mode detail atau mentah)
  • Mengganggu streaming secara interaktif, atau melakukan pemrosesan seperti peta / filter
  • Mendeteksi konvensi baris baru (PC / Mac / Linux)
  • Bukti benar / pengobatan lini terakhir
  • Penanganan karakter multi-byte UTF-8 yang benar
  • Mengambil informasi byte offset dan panjang byte berdasarkan per-line
  • Akses acak, menggunakan offset berbasis garis atau byte
  • Secara otomatis memetakan informasi garis-offset, untuk mempercepat akses acak
  • Tanpa ketergantungan
  • Tes

NIH? Kamu putuskan :-)

panta82
sumber
5
function createLineReader(fileName){
    var EM = require("events").EventEmitter
    var ev = new EM()
    var stream = require("fs").createReadStream(fileName)
    var remainder = null;
    stream.on("data",function(data){
        if(remainder != null){//append newly received data chunk
            var tmp = new Buffer(remainder.length+data.length)
            remainder.copy(tmp)
            data.copy(tmp,remainder.length)
            data = tmp;
        }
        var start = 0;
        for(var i=0; i<data.length; i++){
            if(data[i] == 10){ //\n new line
                var line = data.slice(start,i)
                ev.emit("line", line)
                start = i+1;
            }
        }
        if(start<data.length){
            remainder = data.slice(start);
        }else{
            remainder = null;
        }
    })

    stream.on("end",function(){
        if(null!=remainder) ev.emit("line",remainder)
    })

    return ev
}


//---------main---------------
fileName = process.argv[2]

lineReader = createLineReader(fileName)
lineReader.on("line",function(line){
    console.log(line.toString())
    //console.log("++++++++++++++++++++")
})
pengguna531097
sumber
Saya akan menguji ini, tetapi dapatkah Anda memberi tahu saya, apakah dijamin tidak pernah melanggar karakter multibyte? (UTF-8 / UTF-16)
hippietrail
2
@hippietrail: Jawabannya adalah tidak untuk UTF-8, meskipun itu bekerja pada aliran byte daripada aliran karakter. Terobosan pada baris baru (0x0a). Dalam UTF-8, semua byte dari karakter multibyte memiliki bit set hi-order. Dengan demikian, tidak ada karakter multibyte yang dapat menyertakan baris baru tertanam atau karakter ASCII umum lainnya. UTF-16 dan UTF-32 adalah masalah lain.
George
@ George: Saya pikir kita salah paham satu sama lain. Karena CR dan LF keduanya berada dalam kisaran ASCII dan UTF-8 mempertahankan 128 karakter ASCII tidak berubah, baik CR maupun LF tidak akan pernah dapat menjadi bagian dari karakter UTF-8 multibyte. Yang saya tanyakan adalah apakah datapanggilan untuk stream.on("data")memulai atau mengakhiri hanya dengan sebagian dari karakter UTF-8 multibyte yang U+10D0terdiri dari tiga bytee1 83 90
hippietrail
1
Ini masih memuat seluruh isi file ke dalam memori sebelum menjadikannya "baris baru". Ini tidak MEMBACA satu baris pada satu waktu, melainkan mengambil SEMUA garis dan kemudian memecahnya sesuai dengan panjang buffer "baris baru". Metode ini mengalahkan tujuan membuat aliran.
Justin
Sementara itu ada cara yang lebih sederhana untuk membaca baris dari file, menggunakan readlinemodul inti .
Dan Dascalescu
5

Saya ingin mengatasi masalah yang sama ini, pada dasarnya seperti apa di Perl:

while (<>) {
    process_line($_);
}

Kasus penggunaan saya hanya skrip mandiri, bukan server, jadi sinkron baik-baik saja. Ini adalah kriteria saya:

  • Kode sinkron minimal yang dapat digunakan kembali di banyak proyek.
  • Tidak ada batasan ukuran file atau jumlah baris.
  • Tidak ada batasan panjang garis.
  • Mampu menangani Unicode penuh dalam UTF-8, termasuk karakter di luar BMP.
  • Mampu menangani ujung * nix dan Windows (Mac gaya lama tidak diperlukan untuk saya).
  • Karakter akhir baris untuk dimasukkan dalam baris.
  • Mampu menangani baris terakhir dengan atau tanpa karakter end-of-line.
  • Tidak menggunakan perpustakaan eksternal yang tidak termasuk dalam distribusi node.js.

Ini adalah proyek bagi saya untuk merasakan kode jenis scripting tingkat rendah di node.js dan memutuskan seberapa layak itu sebagai pengganti bahasa scripting lain seperti Perl.

Setelah sejumlah usaha yang mengejutkan dan beberapa kesalahan mulai ini adalah kode yang saya buat. Ini cukup cepat tetapi kurang sepele daripada yang saya harapkan: (bercabang di GitHub)

var fs            = require('fs'),
    StringDecoder = require('string_decoder').StringDecoder,
    util          = require('util');

function lineByLine(fd) {
  var blob = '';
  var blobStart = 0;
  var blobEnd = 0;

  var decoder = new StringDecoder('utf8');

  var CHUNK_SIZE = 16384;
  var chunk = new Buffer(CHUNK_SIZE);

  var eolPos = -1;
  var lastChunk = false;

  var moreLines = true;
  var readMore = true;

  // each line
  while (moreLines) {

    readMore = true;
    // append more chunks from the file onto the end of our blob of text until we have an EOL or EOF
    while (readMore) {

      // do we have a whole line? (with LF)
      eolPos = blob.indexOf('\n', blobStart);

      if (eolPos !== -1) {
        blobEnd = eolPos;
        readMore = false;

      // do we have the last line? (no LF)
      } else if (lastChunk) {
        blobEnd = blob.length;
        readMore = false;

      // otherwise read more
      } else {
        var bytesRead = fs.readSync(fd, chunk, 0, CHUNK_SIZE, null);

        lastChunk = bytesRead !== CHUNK_SIZE;

        blob += decoder.write(chunk.slice(0, bytesRead));
      }
    }

    if (blobStart < blob.length) {
      processLine(blob.substring(blobStart, blobEnd + 1));

      blobStart = blobEnd + 1;

      if (blobStart >= CHUNK_SIZE) {
        // blobStart is in characters, CHUNK_SIZE is in octets
        var freeable = blobStart / CHUNK_SIZE;

        // keep blob from growing indefinitely, not as deterministic as I'd like
        blob = blob.substring(CHUNK_SIZE);
        blobStart -= CHUNK_SIZE;
        blobEnd -= CHUNK_SIZE;
      }
    } else {
      moreLines = false;
    }
  }
}

Mungkin bisa dibersihkan lebih lanjut, itu adalah hasil coba-coba.

hippietrail
sumber
5

Dalam kebanyakan kasus ini seharusnya cukup:

const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, file) => {
  const lines = file.split('\n')

  for (let line of lines)
    console.log(line)
});
Dorian
sumber
2

Pembaca garis berbasis generator: https://github.com/neurosnap/gen-readlines

var fs = require('fs');
var readlines = require('gen-readlines');

fs.open('./file.txt', 'r', function(err, fd) {
  if (err) throw err;
  fs.fstat(fd, function(err, stats) {
    if (err) throw err;

    for (var line of readlines(fd, stats.size)) {
      console.log(line.toString());
    }

  });
});
neurosnap
sumber
2

Jika Anda ingin membaca file baris demi baris dan menulis ini di yang lain:

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

function readFileLineByLine(inputFile, outputFile) {

   var instream = fs.createReadStream(inputFile);
   var outstream = new Stream();
   outstream.readable = true;
   outstream.writable = true;

   var rl = readline.createInterface({
      input: instream,
      output: outstream,
      terminal: false
   });

   rl.on('line', function (line) {
        fs.appendFileSync(outputFile, line + '\n');
   });
};
Thami Bouchnafa
sumber
Apa perbedaan antara jawaban Anda dan kofrasa?
Buffalo
2
var fs = require('fs');

function readfile(name,online,onend,encoding) {
    var bufsize = 1024;
    var buffer = new Buffer(bufsize);
    var bufread = 0;
    var fd = fs.openSync(name,'r');
    var position = 0;
    var eof = false;
    var data = "";
    var lines = 0;

    encoding = encoding || "utf8";

    function readbuf() {
        bufread = fs.readSync(fd,buffer,0,bufsize,position);
        position += bufread;
        eof = bufread ? false : true;
        data += buffer.toString(encoding,0,bufread);
    }

    function getLine() {
        var nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl && eof) return fs.closeSync(fd), online(data,++lines), onend(lines); 
        if (!hasnl && !eof) readbuf(), nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl) return process.nextTick(getLine);
        var line = data.substr(0,nl);
        data = data.substr(nl+1);
        if (data[0] === "\n") data = data.substr(1);
        online(line,++lines);
        process.nextTick(getLine);
    }
    getLine();
}

Saya memiliki masalah yang sama dan muncul dengan solusi di atas terlihat mirip dengan yang lain tetapi aSync dan dapat membaca file besar dengan sangat cepat

Semoga ini bisa membantu

pengguna2056154
sumber
1

Saya memiliki modul kecil yang melakukan ini dengan baik dan digunakan oleh beberapa proyek lain npm readline Catatan thay di node v10 ada modul readline asli jadi saya menerbitkan kembali modul saya sebagai linebyline https://www.npmjs.com/package/ baris demi baris

jika Anda tidak ingin menggunakan modul, fungsinya sangat sederhana:

var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
util = require('util'),
newlines = [
  13, // \r
  10  // \n
];
var readLine = module.exports = function(file, opts) {
if (!(this instanceof readLine)) return new readLine(file);

EventEmitter.call(this);
opts = opts || {};
var self = this,
  line = [],
  lineCount = 0,
  emit = function(line, count) {
    self.emit('line', new Buffer(line).toString(), count);
  };
  this.input = fs.createReadStream(file);
  this.input.on('open', function(fd) {
    self.emit('open', fd);
  })
  .on('data', function(data) {
   for (var i = 0; i < data.length; i++) {
    if (0 <= newlines.indexOf(data[i])) { // Newline char was found.
      lineCount++;
      if (line.length) emit(line, lineCount);
      line = []; // Empty buffer.
     } else {
      line.push(data[i]); // Buffer new line data.
     }
   }
 }).on('error', function(err) {
   self.emit('error', err);
 }).on('end', function() {
  // Emit last line if anything left over since EOF won't trigger it.
  if (line.length){
     lineCount++;
     emit(line, lineCount);
  }
  self.emit('end');
 }).on('close', function() {
   self.emit('close');
 });
};
util.inherits(readLine, EventEmitter);
Maleck13
sumber
1

Solusi lain adalah menjalankan logika melalui nsynjs executor berurutan . Bunyinya file baris demi baris menggunakan modul node readline, dan tidak menggunakan janji atau rekursi, karena itu tidak akan gagal pada file besar. Berikut adalah tampilannya:

var nsynjs = require('nsynjs');
var textFile = require('./wrappers/nodeReadline').textFile; // this file is part of nsynjs

function process(textFile) {

    var fh = new textFile();
    fh.open('path/to/file');
    var s;
    while (typeof(s = fh.readLine(nsynjsCtx).data) != 'undefined')
        console.log(s);
    fh.close();
}

var ctx = nsynjs.run(process,{},textFile,function () {
    console.log('done');
});

Kode di atas didasarkan pada contoh ini: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js

amaksr
sumber
1

Dua pertanyaan yang harus kita tanyakan pada diri sendiri saat melakukan operasi adalah:

  1. Berapa jumlah memori yang digunakan untuk melakukan itu?
  2. Apakah konsumsi memori meningkat secara drastis dengan ukuran file?

Solusi seperti require('fs').readFileSync()memuat seluruh file ke dalam memori. Itu berarti jumlah memori yang diperlukan untuk melakukan operasi akan hampir setara dengan ukuran file. Kita harus menghindari ini untuk hal yang lebih besar dari50mbs

Kita dapat dengan mudah melacak jumlah memori yang digunakan oleh suatu fungsi dengan menempatkan baris kode ini setelah pemanggilan fungsi:

    const used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(
      `The script uses approximately ${Math.round(used * 100) / 100} MB`
    );

Saat ini cara terbaik untuk membaca baris tertentu dari file besar adalah dengan menggunakan node readline . Dokumentasi memiliki contoh luar biasa .

Meskipun kami tidak membutuhkan modul pihak ketiga untuk melakukannya. Tetapi, jika Anda menulis kode perusahaan, Anda harus menangani banyak kasus tepi. Saya harus menulis modul yang sangat ringan yang disebut Apick File Storage untuk menangani semua kasus tepi itu.

Modul Penyimpanan File Apick: https://www.npmjs.com/package/apickfs Dokumentasi: https://github.com/apickjs/apickFS#readme

Contoh file: https://1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx

Contoh: Pasang modul

npm i apickfs
// import module
const apickFileStorage = require('apickfs');
//invoke readByLineNumbers() method
apickFileStorage
  .readByLineNumbers(path.join(__dirname), 'big.txt', [163845])
  .then(d => {
    console.log(d);
  })
  .catch(e => {
    console.log(e);
  });

Metode ini berhasil diuji dengan file padat hingga 4 GB.

big.text adalah file teks padat dengan 163.845 baris dan berukuran 124 Mb. Script untuk membaca 10 baris berbeda dari file ini hanya menggunakan sekitar 4,63 MB Memori saja. Dan mem-parsing JSON yang valid ke Objects atau Array secara gratis. 🥳 Luar Biasa !!

Kita dapat membaca satu baris file atau ratusan baris file dengan konsumsi memori yang sangat sedikit.

vivek agarwal
sumber
0

saya menggunakan ini:

function emitLines(stream, re){
    re = re && /\n/;
    var buffer = '';

    stream.on('data', stream_data);
    stream.on('end', stream_end);

    function stream_data(data){
        buffer += data;
        flush();
    }//stream_data

    function stream_end(){
        if(buffer) stream.emmit('line', buffer);
    }//stream_end


    function flush(){
        var re = /\n/;
        var match;
        while(match = re.exec(buffer)){
            var index = match.index + match[0].length;
            stream.emit('line', buffer.substring(0, index));
            buffer = buffer.substring(index);
            re.lastIndex = 0;
        }
    }//flush

}//emitLines

gunakan fungsi ini pada aliran dan dengarkan acara baris yang akan dipancarkan.

gr-

Elmer
sumber
0

Meskipun Anda mungkin harus menggunakan readlinemodul seperti jawaban atas menyarankan, readlinetampaknya lebih berorientasi pada antarmuka baris perintah daripada membaca garis. Ini juga sedikit lebih buram tentang buffering. (Siapa pun yang membutuhkan pembaca berorientasi garis streaming mungkin ingin mengubah ukuran buffer). Modul readline adalah ~ 1000 baris sementara ini, dengan statistik dan tes, adalah 34.

const EventEmitter = require('events').EventEmitter;
class LineReader extends EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.totalChars = 0;
        this.totalLines = 0;
        this.leftover = '';

        f.on('data', (chunk)=>{
            this.totalChars += chunk.length;
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) lines.pop();
            this.totalLines += lines.length;
            for (let l of lines) this.onLine(l);
        });
        // f.on('error', ()=>{});
        f.on('end', ()=>{console.log('chars', this.totalChars, 'lines', this.totalLines)});
    }
    onLine(l){
        this.emit('line', l);
    }
}
//Command line test
const f = require('fs').createReadStream(process.argv[2], 'utf8');
const delim = process.argv[3];
const lineReader = new LineReader(f, delim);
lineReader.on('line', (line)=> console.log(line));

Inilah versi yang lebih pendek, tanpa statistik, pada 19 baris:

class LineReader extends require('events').EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.leftover = '';
        f.on('data', (chunk)=>{
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) 
                lines.pop();
            for (let l of lines)
                this.emit('line', l);
        });
    }
}
javajosh
sumber
0
const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, data) => {
var innerContent;
    console.log("Asynchronous read: " + data.toString());
    const lines = data.toString().split('\n')
    for (let line of lines)
        innerContent += line + '<br>';


});
Arindam
sumber
0

Saya membungkus seluruh logika pemrosesan garis harian sebagai modul npm: line-kit https://www.npmjs.com/package/line-kit

// example
var count = 0
require('line-kit')(require('fs').createReadStream('/etc/issue'),
                    (line) => { count++; },
                    () => {console.log(`seen ${count} lines`)})

Joyer
sumber
-1

Saya menggunakan kode di bawah ini untuk membaca baris setelah memverifikasi bahwa ini bukan direktori dan tidak termasuk dalam daftar file tidak perlu diperiksa.

(function () {
  var fs = require('fs');
  var glob = require('glob-fs')();
  var path = require('path');
  var result = 0;
  var exclude = ['LICENSE',
    path.join('e2e', 'util', 'db-ca', 'someother-file'),
    path.join('src', 'favicon.ico')];
  var files = [];
  files = glob.readdirSync('**');

  var allFiles = [];

  var patternString = [
    'trade',
    'order',
    'market',
    'securities'
  ];

  files.map((file) => {
    try {
      if (!fs.lstatSync(file).isDirectory() && exclude.indexOf(file) === -1) {
        fs.readFileSync(file).toString().split(/\r?\n/).forEach(function(line){
          patternString.map((pattern) => {
            if (line.indexOf(pattern) !== -1) {
              console.log(file + ' contain `' + pattern + '` in in line "' + line +'";');
              result = 1;
            }
          });
        });
      }
    } catch (e) {
      console.log('Error:', e.stack);
    }
  });
  process.exit(result);

})();
Aniruddha Das
sumber
-1

Saya telah melihat semua jawaban di atas, semuanya menggunakan perpustakaan pihak ketiga untuk menyelesaikannya. Ini memiliki solusi sederhana di API Node. misalnya

const fs= require('fs')

let stream = fs.createReadStream('<filename>', { autoClose: true })

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))
kode mr
sumber