如何用NodeJS从文件底部逐行读取,满足条件即停止且兼容Windows?
从大日志文件底部逐行读取并匹配条件(兼容Windows)
需求概述
需要处理大体积日志文件,从文件末尾开始逐行读取,匹配到特定条件(如JSON格式行的event字段为FSDJump)时立即停止并返回该行;要求兼容Windows系统,且不能将整个文件加载到内存后反转。当前使用的reverse-line-reader包已多年未更新,异步表现不佳,其他相关包存在归档、需提前指定行数或不支持按行处理的问题。
原生Node.js实现方案
可以通过Node.js原生fs模块结合文件指针定位,从文件末尾反向读取块数据,拆分换行符(兼容\n和\r\n)来实现逐行反向读取,无需加载整个文件到内存。以下是封装好的异步实现,完全匹配你的需求:
const fs = require('fs').promises; async function findLastMatchingLine(filePath, matchCondition) { const fileHandle = await fs.open(filePath, 'r'); try { const stats = await fileHandle.stat(); let fileSize = stats.size; const chunkSize = 8192; // 可根据文件大小调整块大小 let remaining = ''; let reachedStart = false; while (!reachedStart) { const start = Math.max(0, fileSize - chunkSize); const chunkLength = fileSize - start; const buffer = Buffer.alloc(chunkLength); await fileHandle.read(buffer, 0, chunkLength, start); // 统一处理Windows和Unix换行符 const chunkStr = buffer.toString('utf8').replace(/\r\n/g, '\n'); const lines = chunkStr.split('\n'); // 拼接块边界的不完整行 if (!reachedStart) { lines[0] = remaining + lines[0]; remaining = lines.pop() || ''; } else { lines.push(remaining); } // 反向遍历当前块的行 for (let i = lines.length - 1; i >= 0; i--) { const line = lines[i].trim(); if (!line) continue; // 跳过空行 try { const parsed = JSON.parse(line); if (matchCondition(parsed)) { return parsed; } } catch (e) { // 非JSON行直接跳过,可根据需求调整处理逻辑 continue; } } reachedStart = start === 0; fileSize = start; } // 遍历完整个文件未找到匹配行 return null; } finally { await fileHandle.close(); } } // 调用示例,完全适配你的业务逻辑 async function init() { try { const result = await findLastMatchingLine(this.#path, (line) => line.event === 'FSDJump'); if (result) { this.location = new System(result); Log.write(`Current location set to ${this.location.name}.`); this.emit('ENTERED_NEW_SYSTEM'); } else { log('Unable to find last hyperspace jump. Searching for last known location.'); } } catch (err) { console.error('读取文件出错:', err); } } init();
实现说明
- 通过文件指针反向读取块数据,避免加载整个文件到内存
- 自动兼容Windows的
\r\n和Unix的\n换行符 - 处理块边界的不完整行,保证每行的完整性
- 逐行反向校验,匹配到目标条件后立即返回,无需遍历整个文件
- 遍历完成未找到匹配项时返回
null,对应原逻辑的提示
推荐第三方包
如果不想自己实现底层逻辑,可以使用@jsdevtools/reverse-readline,这是一个维护活跃的包,支持异步反向逐行读取,兼容Windows系统,无需提前指定行数:
安装
npm install @jsdevtools/reverse-readline
使用示例(匹配你的业务需求)
const ReverseReadline = require('@jsdevtools/reverse-readline'); async function processLogFile(filePath) { const reader = new ReverseReadline({ file: filePath }); for await (const rawLine of reader) { const line = rawLine.trim(); if (!line) continue; try { const parsed = JSON.parse(line); if (parsed.event === 'FSDJump') { this.location = new System(parsed); Log.write(`Current location set to ${this.location.name}.`); this.emit('ENTERED_NEW_SYSTEM'); break; // 匹配到后立即停止读取 } } catch (e) { continue; } } // 遍历完未找到匹配项的提示 log('Unable to find last hyperspace jump. Searching for last known location.'); } processLogFile(this.#path);
这个包内部已经封装了跨平台换行处理、块读取等细节,使用for-await-of异步迭代器,代码更简洁易维护。
内容的提问来源于stack exchange,提问作者punkfairie
相关产品推荐
相关产品推荐

