删除重复行而不排序[重复]
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 代码,昨晚。以上是关于删除重复行而不排序[重复]的主要内容,如果未能解决你的问题,请参考以下文章