2012-05-29 188 views
4

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

对于如何加快速度,您有什么建议吗?我应该使用哪种特定语言?我打算用bash或python做它。

非常感谢。

[编辑1]:对不起,当我说两个文件之间的差异,我不是指差异。结果文件格式不同。

的逻辑是有点像这样:

文件A具有297.599线 文件B具有超过13万线

我挑选当前行从文件中被读出,grep显示它在文件B,如果该行存在于文件B中,我将把它写入结果文件。顺便说一句,文件A和文件B有不同的格式。结果文件将有A文件的格式

[编辑2]:有人问我在工作中创造一个bash理想的解决方案,使我们不必对所有这一切都运行的机器安装python上。

这是我CURENT实现:

#!/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 

这个庆典的做法是采取10小时才能完成。你有什么建议如何使它在bash中更高效?

非常感谢!

+1

使用diff实用程序? – dda

+1

也许如果你展示了一些代码,我们可以帮助优化它。 –

+0

我不太明白你想达到什么目的,但如果你的描述是正确的,那么如果你想对这些文件进行排序,那么它会得到改进。 – vartec

回答

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|)(摊销,由于Pyton's set being extremely fast)。内存需求在O(|B|)中,但有一个因子明显大于1.

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

+0

我认为'print(line)'你的意思'result.write(line)',对吗? –

+2

鉴于文件的大小不对称,我认为你的答案比我的好。 –

+0

@StevenRumbalski好的。固定。 – phihag

4

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

这是一个O(n log n)算法,与您开始的O(n^2)算法相反。

2

你的实现似乎做:

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

但两者@ phihag的和@马克Ronsam的答案是更好的解决方案。另外,如果你想使用沉重的枪支,你的解决方案对于hadoop等map-reduce框架来说是个不错的选择。

2

连接命令也做你想要的。加入需要两个文件被排序在前面。选项-v为每条不可配对的行打印一行。

所以你要像

加入-v 1 sortedfile1 sortedfile2

(您需要根据您的文件格式设置选项的加入,看到加入的联机帮助页)

以下示例使用第二个响应连接文件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 

排序和加入两个工作正常与大文件。

+0

...默认情况下,连接在输出行时将连接列放在前面。使用-o 1.1 1.2 1.3,您可以保留test1.txt的预定顺序。 –

0

我会考虑comm命令。它应该比grep的更快,因为它会与排序的数据工作,而grep的将永远做一个线性搜索

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

您可以通过停止grep稍微加快你的脚本时,找到的第一个比赛,如果这是适合你需要。

如果您的grep支持,请使用。

你的问题是,你产卵grep将近30万次,每次读取超过13,000,000行。

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

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

0

您还可以使用AWK:

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

文件的顺序(... FILE_A FILE_B)在命令行中是非常重要的。

+0

这对我来说什么都没做,结果文件是空的 – user1155413

+0

它对我的示例文件很有效: $ cat file_A aaa bb fff $ cat file_B bb ccc ddd aaa eee # Display records of file_B that are found in file_A: $ awk 'NR==FNR { arr[$0]; next } $0 in arr' file_A file_B bb aaa # Or the other way around: $ awk 'NR==FNR { arr[$0]; next } $0 in arr' file_B file_A aaa bb lind