[英]Sorting a data stream before writing to file in nodejs
我有一个输入文件,可能包含最多1M条记录,每条记录都是这样的
field 1 field 2 field3 \\n
我想读取此输入文件并在将其写入另一个文件之前根据field3
对其进行排序。
这是我到目前为止所拥有的
var fs = require('fs'),
readline = require('readline'),
stream = require('stream');
var start = Date.now();
var outstream = new stream;
outstream.readable = true;
outstream.writable = true;
var rl = readline.createInterface({
input: fs.createReadStream('cross.txt'),
output: outstream,
terminal: false
});
rl.on('line', function(line) {
//var tmp = line.split("\t").reverse().join('\t') + '\n';
//fs.appendFileSync("op_rev.txt", tmp );
// this logic to reverse and then sort is too slow
});
rl.on('close', function() {
var closetime = Date.now();
console.log('Read entirefile. ', (closetime - start)/1000, ' secs');
});
我基本上停留在这一点上,我只有从一个文件读取并写入另一个文件的能力,有没有办法在写入之前有效地对这些数据进行排序
DB
和sort-stream
是很好的解决方案, 但 DB可能是一个过度杀手,我认为sort-stream
最终只是将整个文件排序到内存数组中( through
端回调),所以我认为性能大致相同,与原始解决方案相比。 (但我没有运行任何基准,所以我可能错了)。
所以,只是为了它的黑客,我将投入另一个解决方案:)
编辑:我很想知道这会有多大差异,所以我运行了一些基准测试。
结果令人惊讶,即使对我而言, sort -k3,3
解决方案目前为止比原始解决方案(简单数组排序) 快x10倍 ,而nedb
和sort-stream
解决方案至少比原始解决方案慢x18倍解决方案(即比sort -k3,3
慢至少x180倍)。
(见下面的基准测试结果)
如果在* nix机器(Unix,Linux,Mac,...)上,你可以简单地使用
sort -k 3,3 yourInputFile > op_rev.txt
让操作系统为你做排序。
您可能会获得更好的性能,因为排序是本机完成的。
或者,如果要在Node中处理已排序的输出:
var util = require('util'),
spawn = require('child_process').spawn,
sort = spawn('sort', ['-k3,3', './test.tsv']);
sort.stdout.on('data', function (data) {
// process data
data.toString()
.split('\n')
.map(line => line.split("\t"))
.forEach(record => console.info(`Record: ${record}`));
});
sort.on('exit', function (code) {
if (code) {
// handle error
}
console.log('Done');
});
// optional
sort.stderr.on('data', function (data) {
// handle error...
console.log('stderr: ' + data);
});
希望这可以帮助 :)
编辑:添加一些基准细节。
我很想知道这会有多大的不同,所以我运行了一些基准测试。
以下是结果(在MacBook Pro上运行):
sort1使用简单的方法,对in-memory array
排序。
平均时间: 35.6秒 (基线)
sort2使用sort-stream
,如Joe Krill所建议的那样。
平均时间: 11.1米 (约慢x18.7倍 )
(我想知道为什么。我没有深入挖掘。)
sort3使用nedb
,如Tamas Hegedus所建议的那样。
时间:约16米 (约慢x27倍 )
sort4只能通过在终端中执行sort -k 3,3 input.txt > out4.txt
来sort -k 3,3 input.txt > out4.txt
平均时间: 1.2秒(约快30倍 )
sort5使用sort -k3,3
,并处理发送到stdout的响应
平均时间: 3.65秒 (约快了x9.7倍 )
你可以利用这些流来获得这样的东西。 有一些NPM模块会有所帮助 - 首先通过运行包含它们
npm install sort-stream csv-parse stream-transform
从命令行。
然后:
var fs = require('fs');
var sort = require('sort-stream');
var parse = require('csv-parse');
var transform = require('stream-transform');
// Create a readble stream from the input file.
fs.createReadStream('./cross.txt')
// Use `csv-parse` to parse the input using a tab character (\t) as the
// delimiter. This produces a record for each row which is an array of
// field values.
.pipe(parse({
delimiter: '\t'
}))
// Use `sort-stream` to sort the parsed records on the third field.
.pipe(sort(function (a, b) {
return a[2].localeCompare(b[2]);
}))
// Use `stream-transform` to transform each record (an array of fields) into
// a single tab-delimited string to be output to our destination text file.
.pipe(transform(function(row) {
return row.join('\t') + '\r';
}))
// And finally, output those strings to our destination file.
.pipe(fs.createWriteStream('./cross_sorted.txt'));
您有两种选择,具体取决于正在处理的数据量。 (带有3列的1M记录计数对实际数据量没有太多说明)
var lines = [];
rl.on('line', function(line) {
lines.push(line.split("\t").reverse());
});
rl.on('close', function() {
lines.sort(function(a, b) { return compare(a[0], b[0]); });
// write however you want
fs.writeFileSync(
fileName,
lines.map(function(x) { return x.join("\t"); }).join("\n")
);
function compare(a, b) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
});
使用您选择的数据库引擎(例如nedb , nodejs的纯javascript数据库)
编辑 :似乎NeDB将整个数据库保存在内存中,该文件只是数据的持久副本。 我们必须搜索另一个实现。 TingoDB看起来很有前景。
// This code is only to give an idea, not tested in any way
var Datastore = require('nedb');
var db = new Datastore({
filename: 'path/to/temp/datafile',
autoload: true
});
rl.on('line', function(line) {
var tmp = line.split("\t").reverse();
db.insert({
field0: tmp[0],
field1: tmp[1],
field2: tmp[2]
});
});
rl.on('close', function() {
var cursor = db.find({})
.sort({ field0: 1 }); // sort by field0, ascending
var PAGE_SIZE = 1000;
paginate(0);
function paginate(i) {
cursor.skip(i).take(PAGE_SIZE).exec(function(err, docs) {
// handle errors
var tmp = docs.map(function(o) {
return o.field0 + "\t" + o.field1 + "\t" + o.field2 + "\n";
});
fs.appendFileSync("op_rev.txt", tmp.join(""));
if (docs.length >= PAGE_SIZE) {
paginate(i + PAGE_SIZE);
} else {
// cleanup temp database
}
});
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.