比较两个大文件

时间:2012-05-29 15:03:38

标签: python algorithm bash grep large-files

我需要编写一个程序,将文件写入两个文件之间的差异。 程序必须循环遍历超过13.464.448行的600 MB文件,检查grep是否在另一个文件上返回true然后将结果写入另一个文件。 我写了一个大约1.000.000记录的快速测试,花了一个多小时,所以我猜这种方法可能需要9个多小时。

您对如何加快速度有任何建议吗?我应该使用的任何特定语言?我打算用bash或python做这件事。

提前多多感谢。

[编辑1]:很抱歉,当我说两个文件之间的区别时,我并不意味着差异。结果文件的格式不同。

逻辑有点像这样:

文件A有297.599行 文件B有超过1300万行

我选择从文件A读取的当前行,在文件B上grep它,如果文件B中存在该行,我将把它写入结果文件。顺便说一句,文件A和文件B具有不同的格式。结果文件的格式为文件A.

[编辑2]:我被要求在工作中理想地创建一个bash解决方案,这样我们就不必在必须运行的所有计算机上安装python。

这是我的实施方案:

#!/bin/bash

LAST_TTP=`ls -ltr TTP_*.txt | tail -1 | awk '{ print $9 }'`
LAST_EXP=`ls -ltr *.SSMT | tail -1 | awk '{ print $9 }'`

while read -r line; do
   MATCH="$(grep $line $LAST_EXP)"
   echo "line: $line, match: $MATCH"

   # if not empty
   if [ ! -z "$MATCH" ]
   then
      echo $MATCH >> result
   fi

done < $LAST_TTP

这种bash方法需要10个多小时才能完成。您对如何在bash中提高效率有任何建议吗?

提前多多感谢!

7 个答案:

答案 0 :(得分:9)

您可能正在查找列表而不是集合,从而导致O(n²)性能。尝试:

with open('b') as b:
  blines = set(b)
with open('a') as a:
  with open('result', 'w') as result:
    for line in a:
      if line not in blines:
        result.write(line)

假设统一长(而不是过长的行),此实现的性能在O(|A| + |B|)(由于Python's set being extremely fast而摊销)。内存需求在O(|B|),但有一个显着大于1的因素。

如果输出中的行顺序无关紧要,您还可以对两个文件进行排序,然后逐行进行比较。这将具有大约O(|A| log |A| + B log |B|)的性能。内存需求将在O(|A|+|B|),或更确切地说,|A| + |B|

答案 1 :(得分:4)

对每个输入文件进行排序。现在从每个读取一行。如果一行比较另一行,则将其作为差异输出,并从该文件中读取下一行。如果两条线比较相等,则从两个文件中读取下一行。如果您读到一个文件的末尾,则另一个文件中的所有行都是差异。

这是一个O(n log n)算法,而不是你开始使用的O(n ^ 2)算法。

答案 2 :(得分:2)

您的实施似乎如下:

grep --fixed-strings --file=file_B file_A > result_file

但@ phihag和@mark Ronsam的答案都是更好的解决方案。

另外,如果你想使用重枪,你的解决方案是map-reduce框架的理想选择,例如hadoop。

答案 3 :(得分:2)

join命令也可以执行您想要的操作。 join需要预先对两个文件进行排序。选项-v为每个不可操作的行打印一行。

所以你会想要像

这样的东西

join -v 1 sortedfile1 sortedfile2

(您需要根据文件格式设置连接选项,请参阅联接手册页)

以下示例使用第二个resp连接文件test1.txt和test2.txt。加入的第一个字段。假设使用sort命令对文件进行了预先排序。 -v 1选项仅输出test1.txt无法连接的行。

>cat test1.txt
a 1 2
b 2 3

> cat test2.txt
1 x
4 x

> join -v 1  -1 2  -2 1  test1.txt test2.txt
2 b 3

> join -v 1 -1 2 -2 1 -o 1.1 1.2 1.3 test1.txt test2.txt
b 2 3

排序和连接两者都适用于大文件。

答案 4 :(得分:2)

如果grep找到符合您需求的第一场比赛,您可以停止grep,从而略微加快您的脚本。

如果grep -m 1支持,请使用grep

你的问题是你正在产生grep近300,000次,并且每次读取超过13,000,000行。

在第一场比赛中停止{{1}}会有所帮助,但所有这些高管的开销也是一个重要因素。 Python中的实现将消除此问题。

至于选择脚本中的文件,请参阅BashFAQ/003Parsing ls

答案 5 :(得分:1)

我会考虑comm命令。它应该比grep更快,因为它可以使用已排序的数据,而grep将始终进行线性搜索

comm -2 -3 <(sort file1) <(sort file2)

答案 6 :(得分:0)

您也可以使用awk:

awk 'NR==FNR { arrA[$0]; next } $0 in arrA' file_A file_B > result

命令行中文件(... file_A file_B)的顺序非常重要。