删除重复行而不排序[重复]

Posted

技术标签:

【中文标题】删除重复行而不排序[重复]【英文标题】:Remove duplicate lines without sorting [duplicate] 【发布时间】:2012-07-16 22:40:09 【问题描述】:

我在 Python 中有一个实用程序脚本:

#!/usr/bin/env python
import sys
unique_lines = []
duplicate_lines = []
for line in sys.stdin:
  if line in unique_lines:
    duplicate_lines.append(line)
  else:
    unique_lines.append(line)
    sys.stdout.write(line)
# optionally do something with duplicate_lines

这个简单的功能(uniq 不需要先排序,稳定的排序)必须作为一个简单的 UNIX 实用程序提供,不是吗?也许是管道中过滤器的组合?

询问原因:在无法从任何地方执行 Python 的系统上需要此功能。

【问题讨论】:

不相关:你应该在那个 Python 脚本中真正使用一个集合而不是一个列表;检查列表中的成员资格是一种线性时间操作。 我从您的标签和标题中删除了“Python”,因为这与 Python 无关。 如果这必须在 Python 中完成,更好的方法是使用 uniq_everseen itertools 配方:docs.python.org/library/itertools.html#recipes 【参考方案1】:

UNIX Bash 脚本博客suggests:

awk '!x[$0]++'

这个命令告诉 awk 打印哪些行。变量$0 保存一行的全部内容,方括号是数组访问。因此,对于文件的每一行,数组 x 的节点会递增,如果该节点的内容之前未设置 (!),则会打印该行。

【讨论】:

对于像这样的简短 awk 语句(不涉及大括号),该命令只是告诉 awk 要打印哪些行。变量$0 保存一行的全部内容,方括号是数组访问。因此,对于文件的每一行,我们将增加名为 x 的数组的一个节点,并在该节点的内容之前未设置 (!) 时打印该行。 我翻阅过的最紧凑和最好的脚本。赞一个! 当然,命名该数组会更容易混淆,例如seen 而不是 x,以避免给新手留下 awk 语法是行噪音的印象 请记住,这会将整个文件加载到内存中,因此不要在没有大量 RAM 的 3GB 文本文件上尝试此操作。 @Hitechcomputergeek 这不一定会将整个文件加载到内存中,只会加载唯一的行。如果所有行都是唯一的,这当然可能最终成为整个文件。【参考方案2】:

Michael Hoffman 的上述解决方案简短而实用。对于较大的文件,Schwartzian 变换方法涉及使用 awk 添加索引字段,然后进行多轮排序和 uniq 涉及较少的内存开销。以下 sn-p 在 bash 中工作

awk 'print(NR"\t"$0)' file_name | sort -t$'\t' -k2,2 | uniq --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t'

【讨论】:

这似乎很慢,不过【参考方案3】:

感谢 1_CR!我需要一个“uniq -u”(完全删除重复项)而不是 uniq(保留 1 个重复项)。不能真正修改 awk 和 perl 解决方案来做到这一点,你可以!我可能还需要较低的内存使用,因为我将像 100,000,000 行 8-) 一样唯一。以防万一其他人需要它,我只是在命令的 uniq 部分放了一个“-u”:

awk 'print(NR"\t"$0)' file_name | sort -t$'\t' -k2,2 | uniq -u --skip-fields 1 | sort -k1,1 -t$'\t' | cut -f2 -d$'\t'

【讨论】:

【参考方案4】:

一个迟到的答案 - 我刚刚遇到了这个的副本 - 但也许值得添加......

@1_CR 的答案背后的原理可以写得更简洁,用cat -n 代替awk 加行号:

cat -n file_name | sort -uk2 | sort -n | cut -f2-
使用cat -n 前置行号 使用 sort -u 删除重复数据(-k2 表示“从字段 2 开始作为排序键”) 使用sort -n按前置数字排序 使用cut 删除行号(-f2- 表示“选择字段 2 直到结束”)

【讨论】:

通俗易懂,这通常很有价值。针对上述最短 Michael Hoffman 的解决方案,对大文件的性能有何想法? 更具可读性/可维护性。需要相同但反向排序以仅保留每个唯一值的最后一次出现。在同一排序命令中同时使用--reverse--unique 不会返回预期的结果。显然,sort 通过首先对输入应用--unique 进行过早优化(以减少后续步骤中的处理)。这会过早删除--reverse 步骤所需的数据。要解决此问题,请在管道中插入 sort --reverse -k2 作为第一个排序:cat -n file_name | sort -rk2 | sort -uk2 | sort -nk1 | cut -f2- 一个 900MB 以上的文本文件只用了 60 秒,其中包含如此多(随机放置的)重复行,结果只有 39KB。足够快。 “管道”版本:cat file_name | cat -n | sort -uk2 | sort -nk1 | cut -f2-. “管道”版本用于保留最后一个而不是第一个:cat file_name | cat -n | sort -rk2 | sort -uk2 | sort -nk1 | cut -f2-.【参考方案5】:

我只是想删除以下行中的所有重复项,而不是文件中的所有位置。所以我用了:

awk '
  if ($0 != PREVLINE) print $0;
  PREVLINE=$0;
'

【讨论】:

uniq 不这样做...【参考方案6】:

要从 2 个文件中删除重复项:

awk '!a[$0]++' file1.csv file2.csv

【讨论】:

【参考方案7】:

uniq 命令即使在 http://man7.org/linux/man-pages/man1/uniq.1.html 中也可以使用别名

【讨论】:

【参考方案8】:

现在您可以查看这个用 Rust 编写的小工具:uq。

它执行唯一性过滤,无需先对输入进行排序,因此可以应用于连续流。

与投票率最高的 awk 解决方案和其他基于 shell 的解决方案相比,此工具有两个优点:

    uq 使用它们的哈希值记住行的出现,因此当行很长时它不会使用太多的内存。 uq 可以通过设置要存储的条目数量限制来保持内存使用量不变(当达到限制时,有一个标志可以控制覆盖或终止),而 awk 解决方案可能会遇到行数过多时会出现 OOM。

【讨论】:

相当不方便且不太便携,因为 awk 已经这样做了。 大声笑,我只是想写一个关于这个的 rust 代码,昨晚。

以上是关于删除重复行而不排序[重复]的主要内容,如果未能解决你的问题,请参考以下文章

MySQL连接和连接行而不重复条目[重复]

添加行而不覆盖(Python)[重复]

如何在 React Hooks 中向表中添加行而不重复?

使 SQL 查询返回重复行而不使用 UNION ALL 关闭

显示所有textarea行而不滚动[重复]

组合多个 mySQL 行而不重复