2017-03-02 30 views
1

我需要將兩個文件合併到一個新文件中。perl中非常龐大的聯合陣列

這兩個有超過300百萬個管道分離的記錄,第一列作爲主鍵。行不排序。第二個文件可能有第一個文件沒有的記錄。

示例文件1:

10|X15X1211,J,S,12,15,100.05 

示例文件2:

1231112|AJ32,,,18,JP  
10|AJ15,,,16,PP 

輸出:

10,X15X1211,J,S,12,15,100.05,AJ15,,,16,PP 

我使用下面的代碼段:

tie %hash_REP, 'Tie::File::AsHash', 'rep.in', split => '\|' 
my $counter=0; 
while (($key,$val) = each %hash_REP) { 
    if($counter==0) { 
     print strftime "%a %b %e %H:%M:%S %Y", localtime; 
    } 
} 

準備關聯數組需要將近1小時。 它真的很好,還是真的很糟糕? 有沒有更快的方式來處理關聯數組中的記錄大小?任何腳本語言的任何建議都會有幫助。

感謝, 尼廷T.

我也嘗試下面的程序,walso了1+小時是如下:

#!/usr/bin/perl 
use POSIX qw(strftime); 
my $now_string = strftime "%a %b %e %H:%M:%S %Y", localtime; 
print $now_string . "\n"; 

my %hash; 
open FILE, "APP.in" or die $!; 
while (my $line = <FILE>) { 
    chomp($line); 
     my($key, $val) = split /\|/, $line; 
     $hash{$key} = $val; 
} 
close FILE; 

my $filename = 'report.txt'; 
open(my $fh, '>', $filename) or die "Could not open file '$filename' $!"; 
open FILE, "rep.in" or die $!; 
while (my $line = <FILE>) { 
     chomp($line); 
    my @words = split /\|/, $line; 
    for (my $i=0; $i <= $#words; $i++) { 
    if($i == 0) 
    { 
     next; 
    } 
    print $fh $words[$i] . "|^" 
    } 
    print $fh $hash{$words[0]} . "\n"; 
} 
close FILE; 
close $fh; 
print "done\n"; 

my $now_string = strftime "%a %b %e %H:%M:%S %Y", localtime; 
print $now_string . "\n"; 
+0

請不要標記有無關,與你的問題的語言。如果您要求將此Perl代碼轉換爲Python,那麼堆棧溢出就成爲焦點。 – CoryKramer

+0

任何(perl/python)腳本語言想要的建議 –

+3

這個評論迴應你的糟糕的性能結果:http://cpanratings.perl.org/dist/Tie-File-AsHash – toolic

回答

6

你的技術是有幾個原因非常低效的。

  • 搭售極其緩慢。
  • 你正在把所有東西都拉進內存。

第一個可以通過閱讀和分裂自己來減輕,但後者總是會成爲一個問題。經驗法則是避免將大量數據帶入內存。它會佔用所有的內存,並可能導致它交換到磁盤並放慢速度,尤其是在使用旋轉磁盤的情況下。

取而代之,您可以使用各種「磁盤散列」,例如GDBM_FileBerkleyDB等模塊。

但是,真的沒有理由混淆它們,因爲我們擁有SQLite,它可以做更快更好的一切。


在SQLite中創建一個表。

create table imported (
    id integer, 
    value text 
); 

導入您的文件中使用sqlite的外殼的.import使用.mode.separator調整您的格式。

sqlite>  create table imported (
    ...>   id integer, 
    ...>   value text 
    ...> ); 
sqlite> .mode list 
sqlite> .separator | 
sqlite> .import test.data imported 
sqlite> .mode column 
sqlite> select * from imported; 
12345  NITIN  
12346  NITINfoo 
2398  bar  
9823  baz  

現在你和其他人誰擁有數據的工作,可以做任何你在高效,靈活的SQL它喜歡。即使需要一段時間導入,你也可以在做其他事情的時候去做其他事情。

+2

Re *「你把所有的東西都拉進內存中。」「不,Tie :: File :: AsHash的全部重點在於它*不會*把所有東西都拉到內存中。這是Tie :: File的薄包裝。 – ikegami

+2

重新「*捆綁非常緩慢*」,這也不是問題。問題是從一個Tie :: File :: AsHash哈希獲取一個Tie :: File數組的線性掃描,這意味着每次查找一個哈希元素時,你都會讀取一半的文件(平均)! – ikegami

+2

對我來說,這需要使用'sort'加上Perl合併的兩倍。看到我的答案。 – ikegami

6

我會使用sort非常快速地排序數據(對於10,000,000行5秒),然後合併排序的文件。

perl -e' 
    sub get { 
     my $fh = shift; 
     my $line = <$fh>; 
     return() if !defined($line); 

     chomp($line); 
     return split(/\|/, $line); 
    } 

    sub main { 
     @ARGV == 2 
     or die("usage\n"); 

     open(my $fh1, "-|", "sort", "-n", "-t", "|", $ARGV[0]); 
     open(my $fh2, "-|", "sort", "-n", "-t", "|", $ARGV[1]); 

     my ($key1, $val1) = get($fh1) or return; 
     my ($key2, $val2) = get($fh2) or return; 

     while (1) { 
     if ($key1 < $key2) { ($key1, $val1) = get($fh1) or return; } 
     elsif ($key1 > $key2) { ($key2, $val2) = get($fh2) or return; } 
     else { 
      print("$key1,$val1,$val2\n"); 
      ($key1, $val1) = get($fh1) or return; 
      ($key2, $val2) = get($fh2) or return; 
     } 
     } 
    } 

    main(); 
' file1 file2 >file 

對於每個文件中的10,000,000條記錄,這在慢速機器上花費了37秒。

$ perl -e'printf "%d|%s\n", 10_000_000-$_, "X15X1211,J,S,12,15,100.05" for 1..10_000_000' >file1 

$ perl -e'printf "%d|%s\n", 10_000_000-$_, "AJ15,,,16,PP" for 1..10_000_000' >file2 

$ time perl -e'...' file1 file2 >file 
real 0m37.030s 
user 0m38.261s 
sys  0m1.750s 

或者,可以在數據庫轉儲數據,並讓它處理細節。

sqlite3 <<'EOI' 
CREATE TABLE file1 (id INTEGER, value TEXT); 
CREATE TABLE file2 (id INTEGER, value TEXT); 
.mode list 
.separator | 
.import file1 file1 
.import file2 file2 
.output file 
SELECT file1.id || "," || file1.value || "," || file2.value 
    FROM file1 
    JOIN file2 
    ON file2.id = file1.id; 
.exit 
EOI 

但您付出的靈活性。這花了兩倍的時間。

real 1m14.065s 
user 1m11.009s 
sys  0m2.550s 

注:我本來的.import命令後CREATE INDEX file2_id ON file2 (id);,但刪除它極大地幫助了性能..

+0

對於'10M'記錄,即使花了1.30分鐘處理它,它的效率也非常高。但是,當我進一步搜索'300M'記錄時,由於我的機器上的Temp空間是'5GB',而實際文件是'10GB +',因此在Temp空間中進行了排序,所以它崩潰了。刪除排序部分後,只保留合併部分,提供的文件已排序。它花費了42分鐘來合併,記住'32GB'只有'2GB'可用,並且運行時使用'50%CPU使用率'。殺死所有進程後,耗時24分鐘,'CPU使用率'爲93%。看起來,對於93%的CPU使用率,它仍然較慢! –

+0

即使我試圖將文件分成'30M',每個文件花費了2分鐘。但是當我並行運行所有'10進程'時,它總共花費了12分鐘,但實際上每個進程都是單獨運行2分鐘。 –