本文已参与「新人创作礼」活动,一起开启掘金创作之路。

使用 Node.js 有多种逐行读取文件的方法。在 Node.js 中,文件可以通过同步方式或异步方式读取。使用异步路径,可以在不将文件的所有内容加载到内存中的情况下读取大型文件。

一次读取整个文件将使进程占用大量内存。通过逐行加载并读取文件的能力,它使我们能够根据需要在任何步骤中停止该过程。本文我们将研究使用 Node.js 和内存使用情况比较逐行读取文件的 4 种方法。

Node.js@10+ (最好是最新的 LTS Node 16)。甚至可以在 Docker 上使用 Node.js。

1、同步读取文件

我们可以以同步的方式读取文件,这意味着在内存中加载整个文件并循环读取。但是,因为我们将首先加载整个文件,然后再从中读取任何行,所以内存消耗肯定会超过文件本身大小。下面是一个逐行读取文件的快速示例,但同步方式的性能不是很好:

const fs = require('fs');
const allFileContents = fs.readFileSync('broadband.sql', 'utf-8');
allFileContents.split(/\r?\n/).forEach(line =>  {
    console.log(`Line from file: ${line}`);
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`The script uses approximately ${Math.round(used * 100) / 100} MB`);

因为我们使用的是一个本机的 fs 模块,所以不需要安装任何新的 npm 模块。在上面的代码中,同步读取 while 文件,然后逐行循环通过每一行,并使用 console.log 将其打印到控制台。

循环完成后,打印出大致的内存使用量。

2、Readline

Readline 是一个本地 Node.js 模块,因此不需要安装新的 npm 模块来使用它。它可以通过每次从任何可读流中读取一行来逐行读取文件。我们将使用 on 方法和行事件,当输入流接收到行尾输入 \n、 \r 或 \r\n 时发出行事件。

const events = require('events');
const fs = require('fs');
const readline = require('readline');
(async function processLineByLine() {
    try {
        const rl = readline.createInterface({
            input: fs.createReadStream('broadband.sql'),
            crlfDelay: Infinity
        rl.on('line', (line) => {
            console.log(`Line from file: ${line}`);
        await events.once(rl, 'close');
        console.log('Reading file line by line with readline done.');
        const used = process.memoryUsage().heapUsed / 1024 / 1024;
        console.log(`The script uses approximately ${Math.round(used * 100) / 100} MB`);
    } catch (err) {
        console.error(err);
})();

3、N-readlines

N-readline 是一个 npm 模块,它将逐行读取文件,而不会在内存中缓冲整个文件。它通过使用 Buffer 和本机文件系统模块以块的形式读取文件内容,从而不使用流来实现这一点。即使它以同步的方式工作,它也不会在内存中加载整个文件。

下面是如何使用 N-readline 在使用 npm i --save n-readlines 安装文件后逐行读取文件的示例:

const nReadlines = require('n-readlines');
const broadbandLines = new nReadlines('broadband.sql');
let line;
let lineNumber = 1;
while (line = broadbandLines.next()) {
    console.log(`Line ${lineNumber} has: ${line.toString('ascii')}`);
    lineNumber++;
console.log('end of file.');
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`The script uses approximately ${Math.round(used * 100) / 100} MB`);

4、Line reader

line-reader 模块将自己定义为“异步、缓冲、逐行文件/流阅读器,支持用户定义的行分隔符”。它还提到了 eachLine 函数读取给定文件的每一行。回调中的最后一个变量可用于确定是否已到达文件的最后一行。

使用 npm i --save line-reader 安装该模块,示例代码如下:

const lineReader = require('line-reader');
lineReader.eachLine('broadband.sql', function(line, last) {
    console.log(`Line from file: ${line}`);
    if(last) {
        console.log('Last line printed.');
        const used = process.memoryUsage().heapUsed / 1024 / 1024;
        console.log(`The script uses approximately ${Math.round(used * 100) / 100} MB`);
复制代码
  • 私信