簡體   English   中英

在寫入nodejs中的文件之前對數據流進行排序

[英]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');
});

我基本上停留在這一點上,我只有從一個文件讀取並寫入另一個文件的能力,有沒有辦法在寫入之前有效地對這些數據進行排序

DBsort-stream是很好的解決方案, DB可能是一個過度殺手,我認為sort-stream最終只是將整個文件排序到內存數組中( through端回調),所以我認為性能大致相同,與原始解決方案相比。
(但我沒有運行任何基准,所以我可能錯了)。

所以,只是為了它的黑客,我將投入另一個解決方案:)


編輯:我很想知道這會有多大差異,所以我運行了一些基准測試。

結果令人驚訝,即使對我而言, sort -k3,3解決方案目前為止比原始解決方案(簡單數組排序) 快x10倍 ,而nedbsort-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.txtsort -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;
    }
});

將數據加載到持久數據庫中,讀取有序

使用您選擇的數據庫引擎(例如nedbnodejs的純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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM