0

我有两个文件,每行都有一个 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

但是,我想将此功能添加到我们拥有的(基于 Node 构建的)旨在用于多平台使用的 CLI 中。因此,生成子流程并委托给这些工具不是一种选择。

作为“哑巴”并将每个文件加载到内存中,在换行符.sort()上拆分并调用结果数组的效果非常好(尽管使用了大量内存,但它很快......)但发现差异证明相当困难。

我确信答案在流领域的某个地方,但我缺乏操纵它们的经验,所以我不确定从哪里开始。

使用 Node.js 加载、排序和区分此类大文件的有效技术是什么?

我不是在寻找完整的解决方案(不过,请随意!),在这个阶段,指针会非常有用。

谢谢!

4

2 回答 2

2

最后,我们使用了非常简单的集合,与数组不同,即使有数千个条目,它仍然保持极高的性能和内存效率。这是我们最初的测试代码:

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 工具更快。

于 2016-11-26T19:11:28.767 回答
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' ]
于 2016-11-23T22:05:44.863 回答