删除大文本文件中的所有重复项

时间:2014-03-25 00:51:05

标签: c# java c++ perl duplicates

我真的很难过这个问题,因此我已经停止工作了一段时间。我使用非常大的数据。我每周得到大约200GB的.txt数据。数据范围可达5亿行。其中很多都是重复的。我猜只有20gb是独一无二的。我有几个自定义程序,包括哈希删除重复项,外部删除重复项,但似乎没有工作。最新的一个是使用临时数据库,但需要几天时间才能删除数据。

所有程序的问题在于它们在某一点之后崩溃,并且在这些程序上花了大量资金后,我以为我会上网看看是否有人可以提供帮助。我知道这已经在这里得到了回答,我花了最近3个小时在这里阅读了大约50个线程,但似乎没有一个像我这样的问题,即巨大的数据集。

任何人都可以为我推荐任何东西吗?它需要超级准确和快速。最好不要基于内存,因为我只有32gb的ram可以使用。

2 个答案:

答案 0 :(得分:5)

删除重复项的标准方法是对文件进行排序,然后执行顺序传递以删除重复项。排序5亿行并不是一件容易的事,但它确实可行。几年前,我在一台16 GB的机器上进行了每天50到100 GB的处理。

顺便说一下,你可以用一个现成的程序来做到这一点。当然,GNU排序实用程序可以对大于内存的文件进行排序。我从来没有尝试过500 GB的文件,但你可能会试一试。您可以将其与GNU Core Utilities的其余部分一起下载。该实用程序具有--unique选项,因此您应该只能sort --unique input-file > output-file。它使用类似于我在下面描述的技术。我建议先在100兆字节的文件上尝试,然后慢慢处理更大的文件。

使用GNU排序和我在下面描述的技术,如果输入和临时目录位于不同的物理磁盘上,它将会运行得更好。将输出放在第三个物理磁盘上,或放在与输入相同的物理磁盘上。您希望尽可能减少I / O争用。

可能还有一个商业(即付费)程序将进行排序。开发一个能够有效地对大型文本文件进行排序的程序是一项非常重要的任务。如果你能以几百美元的价格买到东西,那么如果你的时间有价值的话,你可能会提前赚钱。

如果你不能使用现成的程序,那么。 。

如果您的文本包含多个较小的文件,则问题更容易解决。首先,对每个文件进行排序,从这些文件中删除重复项,然后编写已删除重复项的已排序临时文件。然后运行一个简单的n路合并,将文件合并到一个删除了重复项的输出文件中。

如果你有一个文件,你首先要读入尽可能多的行到内存,排序,删除重复文件和编写临时文件。你会继续为整个大文件做这件事。完成后,您可以合并一些已排序的临时文件。

在伪代码中,它看起来像这样:

fileNumber = 0
while not end-of-input
    load as many lines as you can into a list
    sort the list
    filename = "file"+fileNumber
    write sorted list to filename, optionally removing duplicates
    fileNumber = fileNumber + 1

您不必从临时文件中删除重复项,但如果您的唯一数据实际上只占总数的10%,那么您将通过不输出重复数据来节省大量时间临时文件。

一旦写完所有临时文件,就需要合并它们。根据您的描述,我认为您从文件中读取的每个块将包含大约2000万行。因此,您可能有25个临时文件可供使用。

您现在需要进行k-way合并。这是通过创建优先级队列完成的。打开每个文件,从每个文件中读取第一行并将其放入队列以及对其来自的文件的引用。然后,从队列中取出最小的项目并将其写入输出文件。要删除重复项,您需要跟踪输出的上一行,如果新行与上一行相同,则不输出新行。

一旦输出了该行,就会从文件中读取刚刚输出的行的下一行,并将该行添加到优先级队列中。你继续这样做,直到你清空了所有文件。

我在一段时间后发表了一系列关于sorting a very large text file的文章。它使用我上面描述的技术。它唯一没有做的就是删除重复项,但这是对输出临时文件和最终输出方法的方法的简单修改。即使没有优化,该程序也表现良好。它不会设置任何速度记录,但它应该能够在不到12小时内从5亿行中排序和删除重复数据。考虑到第二遍仅使用总数据的一小部分(因为您从临时文件中删除了重复项),可能要少得多。

加速程序可以做的一件事是在较小的块上运行,并在后续线程中对一个块进行排序,同时将下一个块加载到内存中。您最终不得不处理更多临时文件,但这确实不是问题。堆操作稍微慢一点,但是通过将输入和输出与排序重叠来重新捕获额外的时间。您最终获得的I / O基本上是免费的。在典型的硬盘驱动器速度下,加载500千兆字节需要花费两到三个小时的时间。

看一下文章系列。它有很多不同的,大多数是小文章,它们会引导您完成我描述的整个过程,并提供有效的代码。我很乐意回答您可能遇到的任何问题。

答案 1 :(得分:1)

我不是这类算法的专家,但如果它是文本数据(或数字,无所谓),你可以尝试读取你的大文件,并通过前两个或三个符号将其写入多个文件:all以“aaa”开头的行转到aaa.txt,所有行以“aab”开头 - 到aab.txt等。您将获得许多文件,其中数据处于等价关系中:与单词重复是与单词本身在同一个文件中。现在,只需解析内存中的每个文件即可。 再次,不确定它会起作用,但我会首先尝试这种方法......