我试图读取一个大文件一行在一次。我在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之类的东西,但我想弄清楚这个问题。
我不认为其他答案会起作用,因为文件比我运行它的服务器的内存大得多。
从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);
});
最后一行被正确读取(截至Node v0.12或更高版本),即使没有最后的\n。
更新:此示例已添加到Node的API官方文档中。
在进行此类操作时,我们必须问自己两个问题:
执行它需要多少内存?
内存消耗是否随着文件大小的增加而急剧增加?
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。文档中有一些惊人的例子。
我用这个:
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-
你可以自己卷行读卡器。我还没有对这段代码进行基准测试,但它正确地将传入的块流分割成没有结尾的'\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解决方案可能会感兴趣。
我使用下面的代码读取行后,验证它不是一个目录,它不包括在文件列表不需要检查。
(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);
})();