查找一个文件中不存在于另一个文件中的 ID

查找一个文件中不存在于另一个文件中的 ID

我有两个文件:

ABC文件

abcd
xyz
pqrs

目录

zzon
mkno
abcd
  • 我想检查文件中是否存在“abcd”目录
  • 如果“abcd”是第一个,则不一定ABC文件,它还将首次目录
  • 两个文件中都有数千个这样的 ID。
  • 我还想检查有多少个 id 不在目录位于ABC文件

我怎样才能做到这一点 ?

答案1

如果您的目标是找到常见或不常见的线条,comm那么这里将是我的首选命令。

它比较两个文件,并在三列中分别显示文件 1 所特有的行、文件 2 所特有的行以及两个文件中都出现的行。您也可以向其传递标志以抑制任何输出。例如,comm -1 file1 file2将抑制第一列,即文件 1 所特有的内容。comm -12 file1 file2将仅显示两个文件中的内容。

有一个很大的警告:输入必须经过排序。我们可以解决这个问题。

这将显示 abc 中但不在 mno 中的所有内容:

comm -23 <(sort abc.txt) <(sort mno.txt)

您可以将其输入到管道中wc -l以获取计数。


我这样做的原因comm是,一旦文件排序完毕,并排比较在计算上就非常简单了。如果你要处理数百万个这样的文件,那就会有所不同。

这可以通过几个模拟文件来演示。我的电脑速度相当快,因此为了展示不同方法之间的差异,我需要一个相当庞大的样本集。每个文件我都有 1000 万个 10 字符的字符串。

$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > abc.txt
$ cat /dev/urandom | tr -dc '0-9' | fold -w 10 | head -10000000 > mno.txt

$ time comm -23 <(sort abc.txt) <(sort mno.txt) | wc -l
... 0m10.653s

$ time grep -Fcxv -f abc.txt mno.txt
... 0m23.920s

$ time grep -Fcwv -f abc.txt mno.txt
... 0m40.313s

$ time awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt | wc -l
... 0m12.161s

排序是我花费时间最多的事情。如果我们假设 abc.txt 是静态的,我们可以对其进行预排序,这样将来的比较就会快得多:

$ sort abc.txt abc-sorted.txt
$ time comm -23 abc-sorted.txt <(sort mno.txt) | wc -l
... 0m7.426s

您可能会认为几秒钟无关紧要,但我必须强调,这些都是在高端机器上运行的。如果您想在(例如)Raspberry Pi 3 上执行此操作,您将看到更慢的周转时间,并且差异将增加到实际上很重要的程度。

答案2

获取列表:

grep -Fwf abc.txt mno.txt

它会给你类似如下的内容:

abcd
abcd
zef

如果您只想获得一个唯一的列表,那么可以像这样使用它:

grep -Fwf abc.txt mno.txt | sort | uniq

并获取计数:

grep -Fcwv -f abc.txt mno.txt

  • -F意思是:将 PATTERN 解释为固定字符串列表,而不是正则表达式。
  • -f从 FILE 中获取将要得到的模式abc.txt
  • 我们研究mno.txt模式
  • -c计算匹配次数
  • -w仅查找“整个单词”:匹配的子字符串必须位于行首,或者前面有一个非单词构成字符。同样,它必须位于行尾,或者后面有一个非单词构成字符。单词构成字符包括字母、数字和下划线。
  • -v反向搜索

答案3

我们可以使用 awk 来完成这项工作,通过传递两个文件,首先是模式文件,然后是我们要检查的文件。当我们读取第一个文件时,我们知道这一点,NR==FNR并且那时我们可以将行读入数组。当NR!=FNR我们检查是否设置了此行的数组时。

$ cat abc.txt                                                      
abcd
xyz
pqrs
$ cat mno.txt                                                      
zzon
xyz
mkno
abcd
$ awk 'NR==FNR{a[$0]++};NR!=FNR && a[$0]' abc.txt  mno.txt         
xyz
abcd

相反,我们可以否定模式来打印那些不在的行abc.txt

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt       
zzon
mkno

如果我们想打印这些的数量,我们可以sort使用wc

$ awk 'NR==FNR{a[$0]++};NR!=FNR && ! a[$0]' abc.txt  mno.txt | sort -u | wc -l         
2

答案4

如果任一单词列表未排序,则使用高效的集合数据结构来记住常用单词会更快。

Python

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = frozenset(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = frozenset(map(str.rstrip, subtrahend_file))

difference = minuend - subtrahend
#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

用法:

python3 set-difference.py abc.txt mno.txt

Python(更高效)

如果您想为中间存储和运行时间节省一点内存,您可以使用这个稍微难以理解的程序:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as minuend_file:
    minuend = set(map(str.rstrip, minuend_file))
with open(sys.argv[2]) as subtrahend_file:
    subtrahend = map(str.rstrip, subtrahend_file)
    minuend.difference_update(subtrahend)
    difference = minuend
    del minuend

#print(*difference, sep='\n') # This prints the content of the set difference
print(len(difference)) # This prints the magnitude of the set difference

表现

给定abc.txt100mno.txt万行未分类的行,每行 10 个随机 ASCII 数字字符(有关设置,请参阅 Oli 的回答):

$ time python3 set-difference.py abc.txt mno.txt
user    0m10.453s

对阵

$ export LC_COLLATE=C
$ time sort abc.txt > abc_sorted.txt
user    0m10.652s
$ time sort mno.txt > mno_sorted.txt
user    0m10.767s
$ time comm -23 abc_sorted.txt mno_sorted.txt | wc -l
9989882
user    0m1.600s

总计:23 秒

相关内容