如何删除文本文件中的重复行?

Iva*_*van 170 large-files text-processing files

我的一个巨大的(最多 2 GiB)文本文件包含其中每一行的大约 100 个精确副本(在我的情况下没用,因为该文件是一个类似 CSV 的数据表)。

我需要的是在保持原始序列顺序的同时删除所有重复(最好,但可以为了显着的性能提升而牺牲)。结果中的每一行都是唯一的。如果有 100 条相等的行(通常重复项分布在整个文件中并且不会是邻居),则只剩下一种。

我已经用 Scala 编写了一个程序(如果您不了解 Scala,请考虑使用 Java)来实现这一点。但也许有更快的 C 编写的本地工具能够更快地做到这一点?

更新:awk '!seen[$0]++' filename只要文件接近 2 GiB 或更小,该解决方案似乎对我来说很好用,但现在我要清理 8 GiB 文件,它不再起作用。在配备 4 GiB RAM 的 Mac 和配备 4 GiB RAM 和 6 GiB 交换的 64 位 Windows 7 PC 上,似乎无穷无尽,只是内存不足。鉴于这种经验,我并不热衷于在具有 4 GiB RAM 的 Linux 上尝试它。

enz*_*tib 276

awk在#bash (Freenode) 上看到的解决方案:

awk '!seen[$0]++' filename
Run Code Online (Sandbox Code Playgroud)

  • 刚刚在 2G 文件上尝试过,在我的笔记本上花了三分钟。不错。我也尝试过 uniq filename | awk '!seen[$0]++',但速度并没有更快。 (2认同)
  • 想知道这个命令是如何工作的吗?-- 请参见此处:https://unix.stackexchange.com/questions/159695/how-does-awk-a0-work (2认同)
  • @MaxWilliams 是的,它的工作原理是它们是随机分布的。 (2认同)

Gil*_*il' 52

有一种使用标准实用程序的简单(并不是说显而易见的)方法,除了 run 之外不需要大内存sort,在大多数实现中,它对大文件进行了特定优化(一个很好的外部排序算法)。这种方法的一个优点是它只循环特殊用途实用程序中的所有行,而不是在解释语言中。

<input nl -b a -s : |           # number the lines
sort -t : -k 2 -u |             # sort and uniquify ignoring the line numbers
sort -t : -k 1n |               # sort according to the line numbers
cut -d : -f 2- >output          # remove the line numbers
Run Code Online (Sandbox Code Playgroud)

如果所有行都以非空白字符开头,则可以省去一些选项:

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output
Run Code Online (Sandbox Code Playgroud)

对于大量重复,只需要在内存中存储每一行​​的单个副本的方法会表现得更好。有一些解释开销,有一个非常简洁的 awk 脚本(已由 enzotib 发布):

<input awk '!seen[$0]++'
Run Code Online (Sandbox Code Playgroud)

不那么简洁:!seen[$0] {print} {seen[$0] += 1},即如果还没有看到当前行,则打印当前行,然后增加seen该行的计数器(未初始化的变量或数组元素的数值为 0)。

对于长行,您可以通过仅保留每行的不可欺骗校验和(例如加密摘要)来节省内存。例如,使用 SHA-1,您只需要 20 个字节加上每行恒定的开销。但是计算摘要相当慢;只有当您有一个快速的 CPU(尤其是一个带有硬件加速器来计算摘要的 CPU)并且相对于文件的大小和足够长的行没有太多内存时,这种方法才会成功。没有基本的实用程序可以让您计算每一行的校验和;你必须承担 Perl/Python/Ruby/... 的解释开销,或者编写一个专门的编译程序。

<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output
Run Code Online (Sandbox Code Playgroud)


Vla*_*ecs 36

sort -u big-csv-file.csv > duplicates-removed.csv
Run Code Online (Sandbox Code Playgroud)

请注意,输出文件将被排序。

  • 不如其他答案中的“awk”命令那么快,但概念上很简单! (2认同)
  • 使用 `sort -u` 在排序期间而不是之后删除重复项。(并节省内存带宽)将其传输到另一个程序)。如果您也希望对输出进行排序,这只会比 `awk` 版本好。(这个问题上的 OP 希望他的原始排序 * 保留 *,所以这是一个稍微不同的用例的好答案。) (2认同)

Mat*_*Mat 20

假设您有能力在内存中保留与重复数据删除文件一样多的内容(如果您的数据确实重复了 100 倍,那应该是大约 20MiB + 开销),您可以使用 Perl 轻松完成此操作。

$ perl -ne 'print unless $dup{$_}++;' input_file > output_file
Run Code Online (Sandbox Code Playgroud)

这也保留了顺序。

%dup如果您愿意,您可以从散列中提取每行出现的次数,作为额外的免费奖励。

如果您愿意awk,也应该这样做(与 perl 版本相同的逻辑、相同的顺序、在dup变量中收集的相同数据):

$ awk '{if (++dup[$0] == 1) print $0;}' input_file > output_file
Run Code Online (Sandbox Code Playgroud)

  • @dumbledad:`uniq` 自己做这一切 (3认同)

rin*_*eal 10

由于没有其他答案提供就地支持,这里是一个:

gawk -i inplace '!a[$0]++' file
Run Code Online (Sandbox Code Playgroud)