我试图读取一个大文件一行在一次。我在Quora上找到了一个关于这个主题的问题,但我错过了一些联系,把整个事情联系在一起。

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

我想要弄清楚的是如何一次从文件中读取一行,而不是像本例中那样读取STDIN。

我试着:

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

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

但这并不奏效。我知道在必要时我可以使用PHP之类的东西,但我想弄清楚这个问题。

我不认为其他答案会起作用,因为文件比我运行它的服务器的内存大得多。


当前回答

基于生成器的行阅读器: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());
    }

  });
});

其他回答

有一个很好的模块可以逐行读取文件,它叫做行读取器

用它你只需要写:

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

如果你需要更多的控制,你甚至可以用“java风格”界面迭代文件:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});

老话题了,但这个很管用:

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

简单。不需要外部模块。

我有一个小模块,它做得很好,被相当多的其他项目使用npm readline注意,在节点v10中有一个本地readline模块,所以我重新发布了我的模块linebyline https://www.npmjs.com/package/linebyline

如果你不想使用这个模块,函数很简单:

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

在Node.js v18.11.0中添加了一个逐行读取文件的新函数

filehandle.readLines([选项])

这就是如何将此功能用于想要读取的文本文件

import { open } from 'node:fs/promises';
myFileReader();
async function myFileReader() {
    const file = await open('./TextFileName.txt');
    for await (const line of file.readLines()) {
        console.log(line)
    }
}

为了了解更多read Node.js文档,这里有文件系统readlines()的链接: https://nodejs.org/api/fs.html#filehandlereadlinesoptions

当我试图处理这些行并将它们写入另一个流时,我最终使用Lazy逐行读取大量内存泄漏,这是由于节点工作中的drain/pause/resume方式(参见:http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/(我喜欢这个家伙顺便说一句))。我还没有仔细研究Lazy,无法确切地理解其中的原因,但是我无法暂停读流以允许在Lazy退出的情况下进行排泄。

我写了代码来处理大量的csv文件到xml文档,你可以在这里看到代码:https://github.com/j03m/node-csv2xml

如果你用Lazy line运行之前的版本,它就会泄露。最新的版本完全没有泄露,你可以把它作为一个阅读器/处理器的基础。虽然我有一些定制的东西在里面。

编辑:我想我还应该指出,我用Lazy编写的代码工作得很好,直到我发现自己编写了足够大的xml片段,因为必要而耗尽/暂停/恢复。对于较小的块,这是可以的。