在node.js中一次读取一行文件?


551

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

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


2
事实证明,仅使用低级是非常困难的fs.readSync()。您可以将二进制八位位组读取到缓冲区中,但是没有一种简单的方法来处理部分UTF-8或UTF-16字符,而无需先检查缓冲区,然后再将其转换为JavaScript字符串并扫描EOL。该Buffer()类型没有像本机字符串那样丰富的功能集来对其实例进行操作,但是本机字符串不能包含二进制数据。在我看来,缺少一种从任意文件句柄读取文本行的内置方法,是node.js中的真正空白。
hippietrail

5
通过此方法读取的空行将转换为其中包含单个0(实际字符代码为0)的行。我不得不在这里修改这条线:if (line.length==1 && line[0] == 48) special(line);
Thabo 2013年

2
可能还会使用“逐行”软件包,该软件包可以完美地完成工作。
2014年

1
请更新问题,说解决方案是使用转换流
Gabriel Llamas

2
@DanDascalescu(如果愿意)可以将其添加到列表中:您的示例在node的API文档中稍作修改github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

Answers:


787

从Node.js v0.12和Node.js v4.0.0开始,有一个稳定的readline核心模块。这是从文件中读取行的最简单方法,而无需任何外部模块:

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

或者:

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

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

即使没有final,也可以正确读取最后一行(从Node v0.12或更高版本开始)\n

更新:此示例已添加到Node的API官方文档中


7
您需要在createInterface定义中使用terminal:false
glasspill

64
如何确定最后一行?通过捕捉“关闭”事件:rl.on('close', cb)
绿色

27
Readline与GNU Readline具有类似的用途,而不是逐行读取文件。使用它读取文件有一些警告,这不是最佳实践。
赤裸裸的

8
@Nakedible:有趣。您能用更好的方法发布答案吗?
Dan Dascalescu

6
我认为github.com/jahewson/node-byline是逐行阅读的最佳实现,但意见可能会有所不同。
赤裸裸的

164

对于这样的简单操作,不应依赖任何第三方模块。放轻松。

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

33
令人遗憾的是,这种有吸引力的解决方案无法正常工作- line事件只有在命中后才会出现\n,即所有替代项都将丢失(请参阅unicode.org/reports/tr18/#Line_Boundaries)。#2,最后一个之后的数据\n被静默忽略(请参阅stackoverflow.com/questions/18450197/…)。我将这个解决方案称为危险解决方案,因为该解决方案适用于所有文件的99%和数据的99%,但对其余文件却无声无息。每当您fs.writeFileSync( path, lines.join('\n'))编写文件时,上述解决方案只会部分读取该文件。
流动

4
该解决方案存在问题。如果使用your.js <lines.txt,则不会获得最后一行。如果在课程结束时没有'\ n'。
zag2art 2014年

对于readline经验丰富的Unix / Linux程序员,该软件包的行为确实很奇怪。
尖尖的2014年

11
rd.on("close", ..);可以用作回调(读取所有
行时发生

6
我的节点版本(0.12.7)中似乎解决了“最后一个\ n之后的数据”问题。所以我更喜欢这个答案,这似乎是最简单,最优雅的。
Myk Melez

63

您不必访问open文件,而是必须创建一个ReadStream

fs.createReadStream

然后将该流传递给 Lazy


2
是否有像Lazy那样的结束事件?读完所有行后?
最多

1
@Max,尝试:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi 2012年

6
@Cecchi和@Max,不要使用join,因为它将缓冲整个文件在内存中。相反,只听“结束”事件:new lazy(...).lines.forEach(...).on('end', function() {...})
Corin 2012年

3
@ Cecchi,@ Corin和@Max:值得一提的是,.on('end'... 此后 .forEach(...),我疯狂地束缚了自己,而实际上,当我第一次绑定该事件时,一切都按预期运行。
crowjonah's

52
此结果在搜索结果上非常高,因此值得注意的是Lazy看起来已被放弃。到目前为止已经有7个月没有任何变化,并且存在一些令人恐惧的错误(忽略了最后一行,大量内存泄漏等)。
2013年

38

有一个非常好的模块用于逐行读取文件,称为行读取器

使用它,您只需编写:

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

4
这很好。它甚至读取最后一行(!)。值得一提的是,如果它是Windows样式的文本文件,它将保留\ r。line.trim()可以去除多余的\ r。
Pierre-Luc Bertrand 2014年

输入的次优选择只能来自命名文件,而不能(对于一个明显且极为重要的示例process/stdin)。至少,如果可以的话,从阅读代码并尝试来看肯定不是很明显。
尖尖的2014年

2
同时,有一种内置的方法可以使用readline核心模块从文件读取行。
Dan Dascalescu

这是旧的,但万一有人绊倒在它:function(reader)function(line)应该是:function(err,reader)function(err,line)
jallmer

1
仅作记录,line-reader以异步方式读取文件。同步替代方法是line-reader-sync
Prajwal Dhatwalia,

30
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})

42
这将读取内存中的整个文件,然后将其分成几行。这不是问题要问的。关键是能够按需顺序读取大文件。
Dan Dascalescu

2
这适合我的用例,我在寻找一种简单的方法将输入从一个脚本转换为另一种格式。谢谢!
卡拉特

23

2019年更新

一个很棒的示例已经发布在官方的Nodejs文档中。这里

这需要在您的计算机上安装最新的Node.js。> 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();

由于其基于诺言的行为,该答案比上面的任何答案都要好得多,这特别表明了EOF。
phil294

谢谢,太好了。
Goran Stoyanov

3
也许这对于其他人来说是显而易见的,但是我花了一些时间进行调试:如果awaitcreateInterface()调用和for await循环开始之间有任何s ,您将神秘地从文件开始丢失行。createInterface()立即开始在幕后发出线条,并且使用隐式创建的异步迭代器在创建之前const line of rl就无法开始侦听这些线条。
andrewdotn

19

旧主题,但这可行:

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

简单。无需外部模块。


2
如果您获得readline is not definedfs is not defined,请添加var readline = require('readline');var fs = require('fs');使它起作用。否则甜蜜,甜蜜的代码。谢谢。
bergie3000 2015年

12
此答案与之前的答案完全相同,但是没有注释警告,readline程序包被标记为不稳定(从2015年4月开始仍然不稳定),并且在2013年中期,读取文件的最后几行时没有行尾。最后一行是我在v0.10.35中第一次使用它时出现的,然后消失了。/ argh
ruffin 2015年

如果您所做的全部是从文件流中读取的,则无需指定输出。
Dan Dascalescu

18

您可以随时滚动自己的线路阅读器。我尚未对该代码段进行基准测试,但是它可以将传入的数据流正确地分割成几行而没有尾随的'\ 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();

在处理需要在日志解析过程中累积数据的快速日志解析脚本时,我确实想到了这一点,并且我觉得尝试使用js和node而不是使用perl或bash来进行此操作很不错。

无论如何,我的确感到小型的nodejs脚本应该是自包含的,而不是依赖于第三方模块,因此在阅读了该问题的所有答案之后,每个脚本都使用各种模块来处理行解析,因此可能有兴趣使用13 SLOC本机nodejs解决方案。


似乎没有任何简单的方法可以将其扩展为可与任意文件一起使用,除了stdin... 之外,除非我缺少somethign。
hippietrail

3
@hippietrail,您可以创建一个ReadStreamwith fs.createReadStream('./myBigFile.csv')并使用它代替stdin
nolith

2
是否保证每个块都只包含完整的行?是否保证多字节UTF-8字符不会在块边界处分割?
hippietrail

1
@hippietrail我不认为此实现可以正确处理多字节字符。为此,必须首先将缓冲区正确转换为字符串,并跟踪在两个缓冲区之间分割的字符。为此,可以使用内置的StringDecoder
Ernelli 2013年

同时,有一种内置的方法可以使用readline核心模块从文件读取行。
Dan Dascalescu

12

使用载体模块

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});

真好 这也适用于任何输入文件:var inStream = fs.createReadStream('input.txt', {flags:'r'}); 但是您的语法比使用.on()的已记录方法更干净:carrier.carry(inStream).on('line', function(line) { ...
Brent Faust

运营商似乎只处理\r\n\n行尾。如果您需要处理OS X之前的MacOS风格的测试文件,则可以使用它们,\r并且运营商不会处理此文件。出人意料的是,仍然有这样的文件在无处不在。您可能还需要显式处理Unicode BOM(字节顺序标记),这在MS Windows影响范围内的文本文件的开头使用。
hippietrail

同时,有一种内置的方法可以使用readline核心模块从文件读取行。
Dan Dascalescu

9

由于节点中消耗/暂停/恢复的工作方式,我试图使用Lazy逐行读取然后处理这些行并将它们写入另一个流时,最终遇到了大量内存泄漏(请参阅:http:// elegantcode .com / 2011/04/06 / take-baby-steps-with-node-js-pumping-data-between-streams /(我喜欢这个家伙顺便说一句))。我对Lazy的关注不够仔细,无法确切地了解原因,但是我无法暂停我的读取流以允许在Lazy不退出的情况下进行消耗。

我编写了将大量csv文件处理为xml文档的代码,您可以在此处查看代码:https : //github.com/j03m/node-csv2xml

如果您使用“懒行”运行以前的修订版,则会泄漏。最新版本完全没有泄漏,您可以将其用作读取器/处理器的基础。虽然我那里有一些定制的东西。

编辑:我想我还应该注意,在我发现自己编写了足够大的xml片段以进行必要的消耗/暂停/恢复操作之前,使用Lazy的代码运行良好。对于较小的块,这很好。


同时,有一种使用readline核心模块从文件读取行的简单得多的方法。
Dan Dascalescu

对。这是现在的正确方法。但这是从2011年开始的。:)
j03m 2015年


7

自从发布原始答案以来,我发现split是一个非常易于使用的节点模块,用于在文件中进行行读取。也接受可选参数。

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

尚未对非常大的文件进行测试。让我们知道您是否愿意。


6

缺乏针对此问题的全面解决方案令我感到沮丧,因此我进行了自己的尝试(git / npm)。复制粘贴的功能列表:

  • 交互式线路处理(基于回调,不将整个文件加载到RAM中)
  • (可选)返回数组中的所有行(详细或原始模式)
  • 交互式中断流,或执行类似地图/过滤器的处理
  • 检测任何换行符约定(PC / Mac / Linux)
  • 正确的eof /最后行处理
  • 正确处理多字节UTF-8字符
  • 逐行检索字节偏移和字节长度信息
  • 随机访问,使用基于行或基于字节的偏移量
  • 自动映射线偏移信息,以加快随机访问
  • 零依赖
  • 测验

NIH?你决定 :-)


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("++++++++++++++++++++")
})

我将对此进行测试,但是您能告诉我,是否保证不会破坏多字节字符?(UTF-8 / UTF-16)
hippietrail 2013年

2
@hippietrail:对于UTF-8,答案是否定的,即使它在字节流而不是字符流上工作。它在换行符(0x0a)上中断。在UTF-8中,多字节字符的所有字节均设置有高位位。因此,任何多字节字符都不能包含嵌入的换行符或其他常见的ASCII字符。但是,UTF-16和UTF-32是另一回事。
乔治,

@乔治:我认为我们互相误会了。由于CR和LF都在ASCII范围内,并且UTF-8保留了128个ASCII字符不变,因此CR和LF都不能成为多字节UTF-8字符的一部分。我问的是data在调用stream.on("data")今后可能还会开始或用多字节UTF-8字符的仅部分末端,例如这是U+10D0,这三个字节组成e1 83 90
hippietrail

1
这仍然将整个文件内容加载到内存中,然后使其成为“新行”。这不会一次读取一行,而是会占用所有行,然后根据“新行”缓冲区的长度将它们分解。此方法无法实现创建流的目的。
贾斯汀

同时,有一种使用readline核心模块从文件读取行的简单得多的方法。
Dan Dascalescu

5

我想解决同样的问题,在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;
    }
  }
}

可能会进一步清除它,这是反复试验的结果。


5

在大多数情况下,这应该足够了:

const fs = require("fs")

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

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

2

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

  });
});

2

如果要逐行读取文件并将其写入另一个文件:

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

您的回答与kofrasa的回答有什么区别?
水牛城

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

我遇到了同样的问题,提出的上述解决方案对其他人来说看起来很相似,但是它是aSync的,可以非常快速地读取大文件

希望这会有所帮助


1

我有一个能很好地完成此工作的模块,并由其他许多项目使用。npm readline注意节点v10中的thay有一个本机readline模块,因此我将模块重新发布为linebyline https://www.npmjs.com/package/逐行

如果您不想使用该模块,则功能非常简单:

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

1

另一个解决方案是通过顺序执行程序nsynjs运行逻辑。它使用节点readline模块逐行读取文件,并且不使用Promise或递归,因此不会在大文件上失败。代码如下所示:

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

上面的代码基于以下示例:https : //github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js


1

在执行此类操作时,我们必须问自己两个问题:

  1. 用于执行此操作的内存量是多少?
  2. 内存消耗是否随着文件大小而急剧增加?

诸如require('fs').readFileSync()将整个文件加载到内存的解决方案。这意味着执行操作所需的内存量将几乎等于文件大小。我们应该避免这些大于50mbs

通过在函数调用之后放置以下代码行,我们可以轻松跟踪函数使用的内存量

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

现在,从大文件读取特定行的最佳方法是使用node的readline。该文档有一个惊人的例子

尽管我们不需要任何第三方模块。但是,如果您正在编写企业代码,则必须处理很多边缘情况。我必须编写一个非常轻量的模块,称为Apick文件存储来处理所有这些情况。

Apick文件存储模块:https : //www.npmjs.com/package/apickfs 文档:https : //github.com/apickjs/apickFS#readme

示例文件:https : //1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx

示例:安装模块

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

此方法已成功测试多达4 GB的密集文件。

big.text是具有163,845行的密集文本文件,大小为124 Mb。从该文件读取10行不同的脚本仅使用大约4.63 MB内存。并且它免费将有效的JSON解析为对象或数组。🥳太棒了!!

我们可以读取文件的一行或几百行,而只占用很少的内存。


0

我用这个:

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

在流上使用此函数并侦听将要发出的行事件。

gr-


0

虽然您可能应该使用readline模块作为最佳答案,但它readline似乎是针对命令行界面而不是行读取的。在缓冲方面也有些不透明。(任何需要面向流线的阅读器的人都可能希望调整缓冲区大小)。readline模块为〜1000行,而带有统计和测试的行为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));

这是一个更短的版本,没有统计信息,共有19行:

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

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


});


-1

我使用下面的代码,在确认不需要检查它不是目录并且不包括在文件列表中之后,读取行。

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

})();

-1

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

const fs= require('fs')

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

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.