我试图读取一个大文件一行在一次。我在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之类的东西,但我想弄清楚这个问题。

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


当前回答

我对缺乏全面的解决方案感到沮丧,所以我把自己的尝试(git / npm)放在一起。复制粘贴功能列表:

Interactive line processing (callback-based, no loading the entire file into RAM) Optionally, return all lines in an array (detailed or raw mode) Interactively interrupt streaming, or perform map/filter like processing Detect any newline convention (PC/Mac/Linux) Correct eof / last line treatment Correct handling of multi-byte UTF-8 characters Retrieve byte offset and byte length information on per-line basis Random access, using line-based or byte-based offsets Automatically map line-offset information, to speed up random access Zero dependencies Tests

国家卫生研究院?你决定:-)

其他回答

我想解决同样的问题,基本上在Perl中是这样的:

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

我的用例只是一个独立的脚本,而不是服务器,所以同步就可以了。以下是我的标准:

可以在许多项目中重用的最小同步代码。 不限制文件大小或行数。 不限制线的长度。 能够处理UTF-8的完整Unicode,包括BMP以外的字符。 能够处理*nix和Windows行结束符(老式Mac对我来说不需要)。 行中要包含的行结束字符。 能够处理带有或不带有行尾字符的最后一行。 不要使用node.js发行版中不包含的任何外部库。

这是一个让我在node.js中感受低级脚本类型代码的项目,并决定它作为其他脚本语言(如Perl)的替代品的可行性。

经过惊人的努力和一些错误的开始,这是我提出的代码。它非常快,但没有我预期的那么琐碎:(在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;
    }
  }
}

它可能会被进一步清理,这是试验和错误的结果。

我已经看了以上所有的答案,他们都使用第三方库来解决它。它在Node的API中有一个简单的解决方案。如

const fs= require('fs')

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

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

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

用它你只需要写:

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

当我试图处理这些行并将它们写入另一个流时,我最终使用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片段,因为必要而耗尽/暂停/恢复。对于较小的块,这是可以的。

我使用下面的代码读取行后,验证它不是一个目录,它不包括在文件列表不需要检查。

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

})();