2016-11-23 43 views
0

我有兩個文件,每行都有一個UUID。每個文件都有幾十萬行(它們是從數據庫轉儲生成的)。這些文件需要排序並找到差異(添加/刪除)。這是很容易使用一些* nix的工具來完成,只需要幾秒鐘:使用Node.js排序和區分大文件

$ sort file-a.txt > file-a-sorted.txt 
$ sort file-b.txt > file-b-sorted.txt 
$ diff file-a-sorted.txt file-b-sorted.txt 

但是我想這個功能添加到了一種旨在用於多平臺使用,我們(建節點)一個CLI 。因此,產生子流程並委託給這些工具不是一種選擇。

因爲'笨蛋'並將每個文件加載到內存中,在換行符上分割並在生成的數組上調用.sort()的效果驚人地好(雖然使用了相當多的內存,但速度很快......),但發現差異更加困難。

我確定答案在於流的領域,但我缺乏經驗來操縱它們,所以我不確定從哪裏開始。

什麼是使用Node.js加載,排序和區分這些大文件的有效技術?

我不是在尋找完整的解決方案(雖然,感覺自由!),只是在這個階段指針會非常有用。

謝謝!

回答

0

既然你已經在內存中的文件作爲排序陣列,檢出difflib

這似乎完全適合你的使用情況:

>>> difflib.unifiedDiff('one two three four'.split(' '), 
...      'zero one tree four'.split(' '), { 
...      fromfile: 'Original' 
...      tofile: 'Current', 
...      fromfiledate: '2005-01-26 23:30:50', 
...      tofiledate: '2010-04-02 10:20:52', 
...      lineterm: '' 
...      }) 
[ '--- Original\t2005-01-26 23:30:50', 
    '+++ Current\t2010-04-02 10:20:52', 
    '@@ -1,4 +1,4 @@', 
    '+zero', 
    ' one', 
    '-two', 
    '-three', 
    '+tree', 
    ' four' ] 
+0

感謝您抽出時間來推薦此工具 - 非常好=] –

0

在我們去的東西非常簡單的使用套,不像陣列,仍然非常高性能和內存使用效率,甚至包含數千個條目的結束。這是我們最初的測試代碼:

const fs = require('fs') 
const readline = require('readline') 

const memory =() => process.memoryUsage().rss/1048576).toFixed(2) 

const loadFile = (filename, cb) => { 
    // this is more complex that simply calling fs.readFile() but 
    // means we do not have to buffer the whole file in memory 
    return new Promise((resolve, reject) => { 
    const input = fs.createReadStream(filename) 
    const reader = readline.createInterface({ input }) 

    input.on('error', reject) 

    reader.on('line', cb) 
    reader.on('close', resolve) 
    }) 
} 

const start = Date.now() 

const uniqueA = new Set() 
const uniqueB = new Set() 

// when reading the first file add every line to the set 
const handleA = (line) => { 
    uniqueA.add(line) 
} 

// this will leave us with unique lines only 
const handleB = (line) => { 
    if (uniqueA.has(line)) { 
    uniqueA.delete(line) 
    } else { 
    uniqueB.add(line) 
    } 
} 

console.log(`Starting memory: ${memory()}mb`) 

Promise.resolve() 
    .then(() => loadFile('uuids-eu.txt', handleA)) 
    .then(() => { 
    console.log(`${uniqueA.size} items loaded into set`) 
    console.log(`Memory: ${memory()}mb`) 
    }) 
    .then(() => loadFile('uuids-us.txt', handleB)) 
    .then(() => { 
    const end = Date.now() 

    console.log(`Time taken: ${(end - start)/1000}s`) 
    console.log(`Final memory: ${memory()}mb`) 

    console.log('Differences A:', Array.from(uniqueA)) 
    console.log('Differences B:', Array.from(uniqueB)) 
    }) 

這給了我們這個輸出(2011年的Macbook Air):

Starting memory: 19.71mb 
678336 items loaded into set 
Memory: 135.95mb 
Time taken: 1.918s 
Final memory: 167.06mb 
Differences A: [ ... ] 
Differences B: [ ... ] 

使用裝上新行的文件和分裂的「啞巴」方法更快( 〜1.2s),但具有顯着更高的內存開銷(〜2x)。

我們使用Set的解決方案還具有可以跳過排序步驟的優點,使其比原始問題中列出的* nix工具更快。