Node.js - 逐行读取文件的四种方法
使用 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`);
}
});