我的一个巨大的文本文件(最多2 GiB)包含其中每一行的大约100个精确重复项(对我而言,这是无用的,因为该文件是类似CSV的数据表)。需要的是在保持原始序列顺序的同时删除所有重复(最好是牺牲掉,但这可以大大提高性能)。结果,每一行都是唯一的。如果有100条相等的行(通常重复项分布在文件中并且不会成为邻居),则只剩下其中一种。

我已经在Scala中编写了一个程序(考虑Java(如果您不了解Scala的话)来实现这一点。但是,也许有更快的C编写的本机工具能够更快地执行此操作?

更新:只要文件的大小接近2 GiB或更小,awk '!seen[q4312079q]++' filename解决方案对我来说似乎就可以正常工作,但现在我正在清理8 GiB文件,它不再起作用。在配备4 GiB RAM和配备4 GiB RAM和6 GiB交换功能的64位Windows 7 PC的Mac上,似乎占用了无限空间。鉴于这种经验,我并不热衷于在具有4 GiB RAM的Linux上进行尝试。

评论

这会破坏您的订购,但是,您是否尝试过排序-u,我不知道它如何或是否可以在如此大的文件上运行

C通常不会比Java快很多,如果现在(按顺序)运行C,很有可能它会在您得到答案,实现它并完成运行之前完成。乱序排序-u可能会更快。

#1 楼

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

awk '!seen[q4312078q]++' filename


评论


刚刚在2G文件上尝试过,在我的笔记本上花了三分钟。不错。我也尝试过uniq filename | awk'!seen [$ 0] ++',但这没有更快。

– mgjk
2012年1月27日19:27

@HashWizard:此命令不进行排序,但是消除了同一行的下一次出现

– Enzotib
17年5月14日在15:51

想知道此命令如何工作? -请参阅此处:unix.stackexchange.com/questions/159695/how-does-awk-a0-work

– Supergra
17-10-24在19:13

@MaxWilliams是的,可以正常工作,因为它们是随机分布的。

– setholopolus
18年1月19日在19:58

保留换行符或带有空格的行awk'/ ^ \ s *?$ / ||!seen [$ 0] ++'

–詹姆斯·奥布莱恩(James O'Brien)
20 Mar 13 '20 at 0:46



#2 楼

有一个使用标准实用程序的简单方法(这并不是显而易见的方法),除了运行sort之外,该方法不需要大量内存,而!seen[seen] {print} {seen[q4312079q] += 1}在大多数实现中都针对大型文件进行了特定的优化(一种良好的外部排序算法)。此方法的优点是,它仅在专用实用程序内部的所有行上循环,而不会在解释语言内部循环。

空格字符,您可以省去一些选项:

<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


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

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output


简洁:q4312079q,即,如果没有,则打印当前行尚未看到,然后增加该行的q4312079q计数器(未初始化的变量或数组元素的数值为0)。每行的校验和(例如,加密摘要)。例如,使用SHA-1,您只需要20个字节加上每行恒定的开销。但是计算摘要相当慢。仅当您具有快速的CPU(尤其是带有硬件加速器以计算摘要的CPU)并且相对于文件大小和足够长的行没有足够的内存时,此方法才会成功。没有基本的实用程序可让您为每一行计算校验和。您必须承担Perl / Python / Ruby /…的解释开销,或者编写专用的编译程序。

<input awk '!seen[
<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output
]++'


评论


@Gilles根据您对awk'!seen [$ 0] ++'的解释,是否意味着如果awk看到2条重复的行,它将始终保留第一行并忽略所有后续行? (或者它将保留最后一个?)

–user779159
17年5月3日,11:12



@ user779159保留第一个:每个输入行要么立即打印(第一次出现),要么根本不打印(重复出现)。

–吉尔斯'所以-不再是邪恶的'
17年5月3日,11:30

但这与-u ...排序相比如何?

– HashWizard
17年5月13日在21:37

@HashWizard普通排序-u更改顺序。我的答案显示了保留顺序(准确地说是第一次出现的顺序)的解决方案。

–吉尔斯'所以-不再是邪恶的'
17年5月13日在21:42

@Gilles您会说,对于具有50%重复项的大文件(10G),它比sort -u更快?

– HashWizard
17年5月13日在21:43

#3 楼

sort -u big-csv-file.csv > duplicates-removed.csv


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

评论


速度不及其他答案中的awk命令,但从概念上讲很简单!

–约翰
2015年3月31日23:11

@Johann我经常在带有数十万(甚至数百万)短换行符终止字符串的文件上执行此操作。对于正在进行的实验,我很快就能得到结果。如果在反复运行的脚本中使用它可能会更重要,这样可以节省大量时间。

–弗拉迪斯拉夫(Vladislavs Dovgalecs)
15年3月31日在23:13

使用sort -u在排序过程中而不是之后删除重复项。 (并节省内存带宽),将其通过管道传输到另一个程序)。如果您还希望对输出进行排序,则这仅比awk版本好。 (关于此问题的OP希望保留其原始顺序,因此对于稍有不同的用例,这是一个很好的答案。)

– Peter Cordes
2015年9月14日15:39



对我来说,花了大约一分钟的时间来制作550万行文件(总计1.8 GB)。辉煌。

–马克斯·威廉姆斯(Max Williams)
18年1月4日在11:23

#4 楼

假设您有能力在内存中保留尽可能多的已重复数据删除的文件(如果确实确实将数据重复了100倍,那应该是20MiB +开销),则可以使用Perl轻松地做到这一点。 >
$ perl -ne 'print unless $dup{$_}++;' input_file > output_file


这也保留了顺序。

如果需要,您可以从%dup哈希中提取每行的出现次数,作为补充免费赠金。

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

$ awk '{if (++dup[q4312078q] == 1) print q4312078q;}' input_file > output_file


评论


@Mat太好了,我正要把文件lur住,大声笑;-)。

– Nikhil Mulley
2012年1月27日16:10

现在也等待@ManAtWork来获取他的sed和awk魔术织布工:-)

– Nikhil Mulley
2012年1月27日在16:11

再次为awk提示很棒:-)

– Nikhil Mulley
2012年1月27日在16:18

是否可以将perl脚本更改为仅删除重复的相邻行?

– dumbledad
16 Mar 10 '16 at 0:11

@dumbledad:uniq自己完成所有操作

–垫子
16 Mar 10 '16 at 5:50

#5 楼

由于没有其他任何就地支持的答案,所以这里是一个:

gawk -i inplace '!a[q4312078q]++' file


评论


这会保留订单吗?顺便说一句,这对我不起作用。我的版本是:GNU Awk 4.0.2

–狮子座
17-2-16在10:31



@Leonid是的,确实如此。它打印任何唯一行的第一个匹配项。就地支持于2013年发布的4.1版中首次引入。

–rindeal
17年2月16日在12:49

这应该是答案。它实际上是删除现有文件或当前文件中的重复字符串,其中最上面的答案和此处的大多数答案仅打印出uniq /重复的字符串,并且什么也不做,我们必须创建另一个输出来存储结果。

– MaXi32
20 Jun 6'8:33



#6 楼

您可以使用uniq http://www.computerhope.com/unix/uuniq.htm

uniq报告或过滤掉文件中的重复行。

评论


给出答案时,最好对原因做出一些解释。那么,这个答案与之前的几个答案有何不同?

–斯蒂芬·劳赫(Stephen Rauch)
17 Mar 24 '17 at 4:08

在uniq手册页上:注意:“ uniq”不会检测到重复的行,除非它们相邻。因此,您必须首先对其进行排序,然后松开非重复行的顺序。

–温多林
18年11月6日在7:27



#7 楼

Python One衬板:

python -c "import sys; lines = sys.stdin.readlines(); print ''.join(sorted(set(lines)))" < InputFile


评论


这会导致整个文件被拖入内存中,可能无法很好地解决OP的问题。也不能保证保留订单

– iruvar
2013年9月15日14:50

感谢您的建议,我一直在学习python ..只是出于学习目的尝试了此.. :)

–拉胡尔·帕蒂尔(Rahul Patil)
2013年9月15日19:52

这是Python 2.7版本,它不是单行的,而是(简洁地)返回保留行的唯一行,而无需将整个文件加载到内存中或创建单个巨型字符串以供打印

– iruvar
2013年9月16日下午16:37

谢谢@ 1_CR,我今天学到了一些东西:) OrderedDict

–拉胡尔·帕蒂尔(Rahul Patil)
2013年9月16日下午16:39

#8 楼

在我的Mac上,这里没有答案对我有用,所以我写了一个对我有用的简单python脚本。我不理会前导/尾随空格,也不在乎内存消耗。
import sys

inputfile = sys.argv[1]
outputfile = sys.argv[2]

with open(inputfile) as f:
    content = f.readlines()

content = [x.strip() for x in content]

my_list = list(set(content))

with open(outputfile, 'w') as output:
    for item in my_list:
        output.write("%s\n" % item)


#9 楼

无需维护原始序列命令的解决方案
我使用以下代码段进行了处理。
sort duplicates.txt | uniq > noDuplicates.txt

sort命令按字母顺序对行进行排序,而uniq命令则删除重复项。 :为什么我们首先对行进行排序是因为uniq除非相邻行,否则它们不会检测到重复行。

评论


这个问题要求一种方法(最好是)来保持输入顺序。您可以编辑答案来解决这个问题吗?请注意,现有的使用排序的答案保持输入顺序,而使用排序的一个答案不保持输入顺序,但比通过管道传递给uniq更有效。

–斯蒂芬·基特(Stephen Kitt)
20 Sep 7 '14:01

@StephenKitt编辑。我检查了其他答案,但仅使用基本命令找不到任何内容。感谢您的反馈意见。

–ÇağlayanDÖKME
20/09/07在14:18

我给了您一个仅包含基本命令的答案的链接,实际上只有一个命令,排序-u(它是POSIX的一部分);-)。

–斯蒂芬·基特(Stephen Kitt)
20 Sep 7'14:25



@StephenKitt我看到了这个答案。我的也是解决问题的一种方法。您要我做什么?我应该删除答案吗?

–ÇağlayanDÖKME
20年9月7日在15:48

不,不要删除您的答案;我只是想确保您知道其他答案,因为您说过“只有基本命令才能找到任何东西”。

–斯蒂芬·基特(Stephen Kitt)
20年9月7日在15:59

#10 楼

对于bash 4,可以使用利用关联数组的纯bash解决方案。这是一个示例

unset llist; declare -A llist;
while read -r line; do
if [[ ${llist[$line]} ]]; then
  continue
else 
  printf '%s\n' "$line"
  llist[$line]="x"
fi
done < file.txt


评论


不要使用读取循环来处理大文本文件。 bash必须一次读取一个字节,以免换行过头。与awk相比,bash在文本处理方面也不是很快。如果您确实使用了此选项,则读-ra将避免在输入中吃反斜杠。另外,如果将其放在shell函数中或以交互方式使用它,也不要忘记在循环后取消设置llist。

– Peter Cordes
2015年9月14日15:44

@PeterCordes,或者您可能刚刚引用了这个:-)

– iruvar
2015年9月14日20:41在