3.3 文本文件的交集与差集

交集(intersection)和差集(set difference)操作在集合论相关的数学课上经常会被用到。不过,对文本进行类似的操作在某些情况下也很有用。

3.3.1 预备知识

comm命令可用于两个文件之间的比较。它有很多不错的选项可用来调整输出,以便我们执行交集、求差(difference)以及差集操作1

1 假设现在有两个文件A和B,内容分别是:A(1,2,3)B(3,4,5)。那么,对这两个文件进行操作的结果如下。

 交集:3

 求差:1,2,4,5

 差集(A)1,2

  • 交集:打印出两个文件所共有的行。

  • 求差:打印出指定文件所包含的且不相同的那些行。

  • 差集:打印出包含在文件A中,但不包含在其他指定文件中的那些行。

3.3.2 实战演练

需要注意的是comm必须使用排过序的文件作为输入。请看看下面的例子:

  1. $ cat A.txt
  2. apple
  3. orange
  4. gold
  5. silver
  6. steel
  7. iron
  8.  
  9. $ cat B.txt
  10. orange
  11. gold
  12. cookies
  13. carrot
  14.  
  15. $ sort A.txt -o A.txt ; sort B.txt -o B.txt
  16. $ comm A.txt B.txt
  17. apple
  18. carrot
  19. cookies
  20. gold
  21. iron
  22. orange
  23. silver
  24. steel

输出的第一列包含只在A.txt中出现的行,第二列包含只在B.txt中出现的行,第三列包含A.txt和B.txt中相同的行。各列以制表符(\t)作为定界符。

有一些选项可以按照我们的需求进行格式化输出,例如:

  • -1 从输出中删除第一列;

  • -2 从输出中删除第二列;

  • -3 从输出中删除第三列。

为了打印两个文件的交集,我们需要删除第一列和第二列,只打印出第三列:

  1. $ comm A.txt B.txt -1 -2
  2. gold
  3. orange

打印出两个文件中不相同的行:

  1. $ comm A.txt B.txt -3
  2. apple
  3. carrot
  4. cookies
  5. iron
  6. silver
  7. steel

comm命令中用 -3删除第三列,输出第一列和第二列。第一列包含那些只出现在A.txt中的行,第二列包含那些只出现在B.txt中的行。由于输出中只包含了两列,它在此处的用处也不大。对于每个唯一的行,在列中都会对应一个空白字段。因此在同一行上,两列的内容不会重复。为了获得一个可用的输出文本格式,我们需要将空白字段删除,把两列合成一列:

  1. apple
  2. carrot
  3. cookies
  4. iron
  5. silver
  6. steel

要生成这样的输出,就得删除行首的 \t字符。可以用下面的方法删除 \t,从而合并成单列:

  1. $ comm A.txt B.txt -3 | sed 's/^\t//'
  2. apple
  3. carrot
  4. cookies
  5. iron
  6. silver
  7. steel

sed命令通过管道获取comm的输出。它删除行首的 \t字符。sed中的s表示替换(substitute)。/^\t/ 匹配行前的 \t^是行首标记)。//(两个/操作符之间没有任何字符)是用来替换行首的\t的字符串。如此一来,就删除了所有行首的\t

接下来讲解两个文件的差集操作。

差集操作允许你比较两个文件,并打印出只在A.txt或B.txt中出现的行。当A.txt和B.txt作为comm命令的参数时,输出中的第一列是A.txt相对于B.txt的差集,第二列是B.txt相对于A.txt的差集。

通过删除不需要的列,我们就可以分别得到A.txt和B.txt的差集。

  • A.txt的差集
  1. $ comm A.txt B.txt -2 -3

-2 -3 删除第二列和第三列。

  • B.txt的差集
  1. $ comm A.txt B.txt -1 -3

-1 -3 删除第一列和第三列。