2012-05-29 90 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