我有两个文件,每行都有一个UUID。每个文件有几十万行(它们是从数据库转储生成的)。需要对这些文件进行排序并找到差异(添加/删除)。使用一些* nix工具很容易做到这一点,只需几秒钟:
$ 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(构建在Node上),用于多平台使用。因此,产生子流程并委托给这些工具不是一种选择。
“愚蠢”并将每个文件加载到内存中,拆分换行符并在生成的数组上调用.sort()
的工作效果非常好(尽管使用了大量的内存,但速度很快......)但找到差异是证明更难。
我确定答案就在溪流的某个地方,但我缺乏操纵它们的经验,所以我不确定从哪里开始。
使用Node.js加载,排序和区分大型文件的有效技术是什么?
我不是在寻找完整的解决方案(但是,请随意!),只是指针在这个阶段非常有用。
谢谢!
答案 0 :(得分: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' ]
答案 1 :(得分: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: [ ... ]
使用' dumb'加载文件和拆分换行的方法甚至更快(~1.2s),但内存开销明显更高(~2x)。
我们使用Set
的解决方案还有一个优势,即我们可以跳过排序步骤,使其比原始问题中概述的* nix工具更快。